October 22, 2024
Chicago 12, Melborne City, USA
javascript

How do I externally reset the state of a controlled downshift component


I have a downshift typeahead component that allows a user to select options from a dropdown that will autocomplete the rest of the fields in the form using react-final-form. If the user input is not present in the pre-existing mortgagees, there will be an option to create a new object and enter a sort of "creating mode"

Example:

const ComboBox = ({
  ...exampleProps,
  value="",
  isCreating = false,
}) => {
  const [selectedItem, setSelectedItem] = useState(null);   // CONTROLLED STATE
  // ... other state / initial declarations

  useEffect(() => {
    if (value === '' && !isCreating) {
      setSelectedItem(null);
      selectItem(null);
    }
  }
}

This useEffect is primarily utilized by a "reset" button in the parent component that calls react-final-form‘s form.restart() and calls setIsCreating(false)

This works perfectly fine in every case but one:
if the user is creating they can type to change the input value and reset the form to their heart’s content. They can also backspace their input until it is equal to ” while STILL being in that creating state. This is desired.

The problem is that now the input value is equal to '', and thus the useEffect will not be called and the internally controlled selectedItem will not be reset when the user clicks the reset button.

Here is the stateReducer for reference:

    switch (type) {
      case useCombobox.stateChangeTypes.InputKeyDownEnter:
      case useCombobox.stateChangeTypes.ItemClick:
      case useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem:
      case useCombobox.stateChangeTypes.FunctionSelectItem:
      case useCombobox.stateChangeTypes.InputBlur:
        if (changes?.selectedItem?.creatable) {
          return {
            ...changes,
            inputValue: state.inputValue,
            selectedItem: { ...changes.selectedItem, label: inputValue },
          };
        } else if (changes?.selectedItem && selectionLabelKey) {
          return {
            ...changes,
            inputValue: changes.selectedItem[selectionLabelKey] || '',
          };
        } else return changes;
      case useCombobox.stateChangeTypes.InputChange:
      case useCombobox.stateChangeTypes.InputClick:
        if (changes?.selectedItem?.creatable) {
          return {
            ...changes,
            isOpen: false,
            selectedItem: {
              ...changes.selectedItem,
              label: changes.inputValue,
            },
          };
        } else return changes;
      default:
        return changes;
    }

In onSelectedItemChange is where I am handling the synchronization of downshift state and controlled state. Like I said, this is weird and pretty complicated in my opinion.

The components are pretty complex and specific, so I am using a stateReducer as well as onSelectedItemChange and onInputValueChange.

I cannot seem to utilize these callbacks to actually fix the aforementioned problem. The user cannot backspace themselves into an empty combobox while creating a new option, and expect the form to reset like normal. The internal downshift state is not changed.

Is there a way to remedy this without reinitializing the downshift component entirely?



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video