downshift-js / downshift

🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.
http://downshift-js.com/
MIT License
12.11k stars 932 forks source link

useMultipleSelection: Can't remove items when selectedItems is controlled #1068

Closed andrewn closed 4 years ago

andrewn commented 4 years ago

Relevant code or config

  const {
    getSelectedItemProps,
    getDropdownProps,
    addSelectedItem,
    removeSelectedItem
  } = useMultipleSelection({
    selectedItems, // selectedItems is controlled outside the component
    onSelectedItemsChange // this is not called when removeSelectedItem() is used
  });

What you did:

I'm using useMultipleSelection with selectedItems in controlled mode. This is so I can have external forms drive the Combobox state.

What happened:

Calling removeSelectedItem(item) to remove an item in the selection doesn't call onSelectedItemsChange and so the controlled selectedItems is never updated. The result is that the item cannot be removed.

Reproduction repository:

I forked the useMultipleSelection demo.

Problem description:

I added a stateReducer to log what happens. Clicking on the item that calls removeSelectedItem(item) causes the following reducer changes, in order:

type state.selectedItems changes.selectedItems
__function_remove_selected_item__ ["Neptunium", "Plutonium", "Dubnium"] ["Plutonium", "Dubnium"]
selected_item_click ["Neptunium", "Plutonium", "Dubnium"] ["Neptunium", "Plutonium", "Dubnium"]

Suggested solution:

It seems that the click action is overwriting the result of removing the selected item.

ben-styling commented 4 years ago

I got around this issue by moving the remove element with the click handler to remove outside of the element with getSelectedItemProps

See below:

{selectedItems.map((selectedItem, index) => (
  <>
    <span
      style={selectedItemStyles}
      key={`selected-item-${index}`}
      {...getSelectedItemProps({ selectedItem, index })}
    >
      {selectedItem}
    </span>
    <span
      style={selectedItemIconStyles}
      onClick={() => removeSelectedItem(selectedItem)}
    >
      &#10005;
    </span>
  </>
))}
silviuaavram commented 4 years ago

Hi! I updated the example to include a stop propagation on the click event, as it was caught by the handler in the item otherwise. Can you update your use case to reflect this?

andrewn commented 4 years ago

Hi @silviuaavram, I don't see your updated example?

ben-styling commented 4 years ago

@andrewn Add onClick={e => { e.stopPropagation() }} to your span element with getSelectedItemProps on it.

https://codesandbox.io/s/usemultipleselection-combobox-usage-8enbh?file=/src/index.js

silviuaavram commented 4 years ago

or here https://www.downshift-js.com/use-multiple-selection#usage-with-combobox