Reputation: 37108
Another developer came to me with a tricky question today. She proposed the following:
A.) She wants to fire an event whenever a modal closes.
B.) She does not want the event to fire at any other time.
C.) The event must be up to date with state of the component.
A basic example is like so:
const ModalComponent = () => {
const [ eventData, setEventData ] = useState({ /* default data */ });
useEffect(() => {
return () => {
// happens anytime dependency array changes
eventFire(eventdata);
};
}, [eventData]);
return (
<>
// component details omitted, setEventData used in here
</>
);
};
The intention is that when the modal is closed, the event fires. However, user interactions with the modal cause state updates that change the value of eventData
. This leads to the core of the problem:
eventData
out of the useEffect
dependency array causes it to fire only at time of modal closing, but the value is stale. It's the value that it was at the time the component mounted.eventData
in the useEffect
dependency array causes the event to fire over and over, whenever the data changes and the component re-renders and updates useEffect
.What is the solution to this? How can you access up-to-date data yet only act on it at time of unmounting?
Upvotes: 5
Views: 1457
Reputation: 475
Store eventData
in a ref.
const [eventData, setEventData] = useState({ /* default data */ });
const ref = useRef();
ref.current = eventData;
useEffect(() => () => eventFire(ref.current), []);
This will keep the value always up to date since it won't be locked into the function closure and will remove the need for triggering the effect every time eventData
changes.
Also, you can extract this logic into a custom hook.
function useStateMonitor(state) {
const ref = useRef();
ref.current = state;
return () => ref.current;
}
And usage would be like this
const [eventData, setEventData] = useState({ /* default data */ });
const eventDataMonitor = useStateMonitor(eventData);
useEffect(() => () => eventFire(eventDataMonitor()), []);
Here's a working example
Upvotes: 5