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.
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()
.