benbowes / react-responsive-select

A customisable, touchable, React select / multi-select form control. Built with keyboard and screen reader accessibility in mind
https://benbowes.github.io/react-responsive-select/
MIT License
108 stars 17 forks source link

onListen wrongly invoked with `isOpen=false` #170

Open loopmode opened 3 years ago

loopmode commented 3 years ago

When you change the selected option in a multiselect scenario, the onListen callback is invoked and claims that isOpen is false, while it should still remain true (The user keeps the overlay visible and changes which options are checked, but he does not close the overlay)

Technically, the user dispatches SET_MULTISELECT_OPTIONS actions, and that action type is not handled in this section: https://github.com/benbowes/react-responsive-select/blob/755f30b947da8d64d7a8c9ea55d93d501c8ce005/src/react-responsive-select.tsx#L84-L89

Thus, the onListen callback is invoked with isOpen=false instead of either not being called at all, or called with isOpen=true.

loopmode commented 3 years ago

See reproducible example https://codesandbox.io/s/react-multiple-select-issue-jcq1e

Open console, change options and observe log output.

loopmode commented 3 years ago

A way to work around the problem, based on this recipe, is to only handle the isOpen change on actions that explicitly change the panel open state, instead of on just any action:

  const prevIsOpen = React.useRef(false);

  const handleSelectStatus = React.useCallback((isOpen, name, action) => {
    if (
      action === 'SET_OPTIONS_PANEL_OPEN' ||
      action.startsWith('SET_OPTIONS_PANEL_CLOSED')
    ) {
      if (isOpen && prevIsOpen.current !== isOpen) {
        setOverlayVisible(isOpen);
        prevIsOpen.current = isOpen;
      } else if (!isOpen && prevIsOpen.current !== isOpen) {
        setOverlayVisible(isOpen);
        prevIsOpen.current = isOpen;
      }
    }
  }, []);