I have an event handler function which every time is triggered should clear a setTimeout()
and set a new timeout.
Now the callback for the timeout has to take few arguments, for example the event object received by the handler.
And that's it. I did it, but I believe that my solution is not optimal, nor the cleanest:
let timer;
const handler = (event, type) => {
clearTimeout(timer);
if(type === 'CHANGE') timer = setTimeout(callback, 500, { argumentsForTheCallBack });
}
As you can see I have my timer
as a global variable, even though I need it only in the event handler.
Is there a way to make the handler self-contained?
I am interested in ECMAScript solutions as well as solution using React.js syntax (in my original code the callback is a dispatcher for a useReducer
hook)
1 Answer 1
Since you mention useReducer
React hook I will assume you are using a functional component and thus can use other React hooks.
I suggest:
- Use a React ref to hold a reference to the timer (versus global variable).
- Use an
useEffect
hook to clear any running timeouts in the case the component unmounts before the timeout expires. Provide an empty dependency array so the effect callback is called only once. Here it is just to return the cleanup function to clear any running timeouts.
Code:
import { useEffect, useRef } from 'react';
...
// in component
const timerRef = useRef(null);
useEffect(() => {
return () => clearTimeout(timerRef.current);
}, []);
const handler = (event, type) => {
clearTimeout(timerRef.current);
if (type === 'CHANGE') {
timerRef.current = setTimeout(callback, 500, { argumentsForTheCallBack });
}
}
Why provide cleanup function from useEffect
?
If you attempt to update state of an unmounted component you'll get a React warning stating:
Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
It's just a warning though, so if you are ok with the warning in non-production builds and you know you don't actually have a memory leak (open sockets, connections, subscriptions, etc...) then you can likely safely ignore them.
In general you should strive to write code that doesn't generate warnings though, and this is a warning that is easily preventable.
-
1\$\begingroup\$ Thanks for the answer. I am fairly new to React and as far as I know this
useEffect
with an empty array as dependency should run only one time, when the app starts running. What am I missing? \$\endgroup\$anotherOne– anotherOne2021年04月24日 13:41:37 +00:00Commented Apr 24, 2021 at 13:41 -
1\$\begingroup\$ @SheikYerbouti Yes, that is exactly the intention. I updated answer to provide this additional information for clarity and purpose. \$\endgroup\$Drew Reese– Drew Reese2021年04月24日 17:43:00 +00:00Commented Apr 24, 2021 at 17:43
-
1\$\begingroup\$ @SheikYerbouti Added response to your follow-on question in an update in my answer. \$\endgroup\$Drew Reese– Drew Reese2021年04月24日 18:02:45 +00:00Commented Apr 24, 2021 at 18:02
-
1\$\begingroup\$ Thank you very much for the explanation. Now I got that I should clear any asynchronous task, but wasn't the
useEffect
executed only once at the beginning? Is it automatically called again when the component unmounts? And if yes, is this true only foruseEffect
s with an empty array as dependency? \$\endgroup\$anotherOne– anotherOne2021年04月24日 18:11:43 +00:00Commented Apr 24, 2021 at 18:11 -
1\$\begingroup\$ @SheikYerbouti In this case, yes, the
useEffect
hook is called at the end of the initial render, and the effect cleanup will be called when the component unmounts. Generally, the cleanup function is called at the end of the render cycle before the component is rerendered (to clean up any effects from the previous render) or when the component is unmounting. The effect with empty dependency and returned cleanup function is roughly equivalent to thecomponentDidMount
andcomponentWillUnmount
lifecycle methods. See reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect. \$\endgroup\$Drew Reese– Drew Reese2021年04月24日 18:17:32 +00:00Commented Apr 24, 2021 at 18:17