Downshift: [useCombobox] Cursor jumps to end of input when using controlled value

Created on 2 Jul 2020  路  4Comments  路  Source: downshift-js/downshift

  • downshift version: 5.45
  • node version: 13.12
  • npm (or yarn) version: 6.14.4

Relevant code or config

  const [value, setValue] = useState("");
  const { getInputProps } = useCombobox({
    items: [],
    inputValue: value,
    onInputValueChange: ({ inputValue }) => {
      setValue(inputValue);
    }
  });

  return <input {...getInputProps()} />;

What you did:
I am passing a value into inputValue and update it using onInputValueChange.

What happened:
The cursor always jumps to the end of the input.

Reproduction repository:
I created a minimal example here: https://codesandbox.io/s/usecombobox-controlled-input-issue-vj8o6?file=/src/index.js

Problem description:
It seems that there are two consecutive re-renders happening on keydown, the first one with the old value (resetting the input to it鈥檚 state before the keypress), and a second render with the new value, moving the cursor to the end. I saw that there was a similar issue in #217, but maybe unrelated?

Suggested solution:
I also created a similar example without using downshift here. It seems, that we either need any keypress to only trigger one re-render (ideal) or make sure the first one contains the up-to date input value.

bug help wanted

Most helpful comment

Simple fix on the client side:

const [value, setValue] = useState("");
  const { getInputProps } = useCombobox({
    items: [],
    inputValue: value
  });

  return <input {...getInputProps({
                onChange: e => {
                  setInputValue(e.target.value)
                }
  })} />;             

It is not possible to call setValue inside onInputValueChange with the current way downshift is calling that asynchronously. This is too late for React since the input value first will be reset to the previous value, and only after that set to the new value, which causes the curser position to be reset. It also causes issues with deadkeys.

All 4 comments

Hi! It may be related, found this in downshift.js:

    // we want to call `onInputValueChange` before the `setState` call
    // so someone controlling the `inputValue` state gets notified of
    // the input change as soon as possible. This avoids issues with
    // preserving the cursor position.
    // See https://github.com/downshift-js/downshift/issues/217 for more info.
    if (!isStateToSetFunction && stateToSet.hasOwnProperty('inputValue')) {
      this.props.onInputValueChange(stateToSet.inputValue, {
        ...this.getStateAndHelpers(),
        ...stateToSet,
      })
    }

We may need something similar here as well. i probably will hate it, since I tried to create a unified state reducer - on change callers for all state props, and this will be different. Can you submit a fix please?

Simple fix on the client side:

const [value, setValue] = useState("");
  const { getInputProps } = useCombobox({
    items: [],
    inputValue: value
  });

  return <input {...getInputProps({
                onChange: e => {
                  setInputValue(e.target.value)
                }
  })} />;             

It is not possible to call setValue inside onInputValueChange with the current way downshift is calling that asynchronously. This is too late for React since the input value first will be reset to the previous value, and only after that set to the new value, which causes the curser position to be reset. It also causes issues with deadkeys.

You can also avoid any downshift hack and control the cursor position yourself. Just have a state value with the cursor position value, which you get from the input onChange, and make sure it keeps its value.

It's unfortunate to have such hacks, but calling onInputValueChange or onStateChange before setting the state is also a hack.

@fabb I'm doing this and it helps me work around the issue and also avoid a double render, but it's a bummer that I now have to reimplement a lot of Downshift's input value handling.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

slavab89 picture slavab89  路  3Comments

Vincent-Alibert picture Vincent-Alibert  路  4Comments

kentcdodds picture kentcdodds  路  3Comments

alexandernanberg picture alexandernanberg  路  4Comments

srishanbhattarai picture srishanbhattarai  路  3Comments