Search code examples
javascriptreactjsreact-hooksreact-context

How to use the most updated state/value inside a 3rd party event handler?


Given something like:

function MapControl() {
  const [countries, setCountries] = useContext(CountriesContext)
  useEffect( () => {
    ThirdPartyApi.OnSelectCountry((country) => {
      setCountries([...countries, country])
    })
  })

  return (<ThirdPartyApi.Map />)
}

I have the problem that the call to setCountries doesn't work as expected, because the countries array is not updated from the context of the custom event handler provided by ThirdPartyApi.

What is a clean way to model this? It's possible to just have a local mutable array that I update in the event handler, but that wouldn't pick up any changes to countries from other components, so feels doomed to cause problems.


Solution

  • You can use a functional update to modify your state using the latest value instead of capturing it from the stale closure:

    function MapControl() {
      const [countries, setCountries] = useContext(CountriesContext)
    
      useEffect( () => {
        ThirdPartyApi.OnSelectCountry((country) => {
          setCountries((prev) => [...prev, country])
        })
    
        return () => {
          // unregister event handler
        }
      }, [])
    
      return (
        <ThirdPartyApi.Map />
      )
    }
    

    Also make sure to specify your dependencies for useEffect() so that you're not triggering your side-effect on every re-render. In this case, your side effect doesn't have any dependencies, so it should be empty [].

    Lastly, make sure to clean up your effect when the component unmounts. In this case you need to unregister your event handler within the returned callback from useEffect().