react-select does not “fire” the `onChange` handler

I have a DropDown component that renders a react-select dropdown and utilizes a custom hook in order to open/close.

Custom Hook

import { useState, useRef, useEffect } from 'react';

const useCloseOnClickOutside = (initialState) => {
  const [isVisible, setIsVisible] = useState(initialState);
  const ref = useRef(null);

  /**
   * Attach Event Listeners
   */
  useEffect(() => {
    document.addEventListener('click', handleClick, true);
    document.addEventListener('touchstart', handleClick, true);
    return () => {
      document.removeEventListener('click', handleClick, true);
      document.removeEventListener('touchstart', handleClick, true);
    };
  },[]);


  const handleClick = (event) => {
    if (!ref.current)
      return;

    // If someone clicks outside the component close the state
    if (!ref.current?.contains(event.target)) {
      setIsVisible(false);
    } else {
      // If someone click on the component toggle the state
      setIsVisible((prevState => !prevState));
    }
  };

  return {
    ref,
    isVisible
  };

};

export default useCloseOnClickOutside;

Dropdown

import React from 'react';
import Select from 'react-select';
import useCloseOnClickOutside from '../../hooks/useCloseOnClickOutside';

const Dropdown = ({handleChange, defaultValue, options, value}) => {
  const {ref,isVisible} = useCloseOnClickOutside(false);

  return (
    <div ref={ref}>
      <Select
        options={options}
        onChange={handleChange}
        defaultValue={defaultValue}
        value={value}
        menuIsOpen={isVisible}
      />
    </div>
  );
};

I pass the ref from useCloseOnClickOutside to an outer div of Select in order to catch the bubble events.

The handleChange callback that I pass to Select onChange does nothing at the moment:

const handleChange = (val) => console.log(val)

And call it like this:

Parent Component

const ParentComponent = () => {

// All theese are just mocked data
const mockedOptions = [];
const handleChange = (val) => console.log(val);
const mockedDefaultValue = 'default-value';
const mockedValue = stateValue;

return (
  <Dropdown 
  handleChange={handleChange} 
  options={mockedOptions}
  defaultValude={mockedDefaultValue}
  value={mockedValue}
/>
)
}

The issue is that at the moment I pass the ref to the parent div, onChange does not fire at all. If I remove it, it runs successfully. I cant find any reason why this should be happening. Could this be an issue of react-select ??

Answer

I saw the problem is you are using the capture pharse in the addEventListener. So setIsVisible(false); will be call before onChange. So onChange will not call when select was hidden.

I saw you have two solution to fix.

Remove capture phase:

document.addEventListener("click", handleClick);

Or delay setTimeOut to make sure it call after onChange:

setTimeout(() => setIsVisible(false), 0);