I have a component, StatusIndicator
, that uses a Redux Toolkit query, q
, to fetch a status value and display it.
I want to write a custom hook, usePolling
, that can be configured using arguments to invoke q
at intervals, updating StatusIndicator
.
I have two problems:
- Even though I specify a dependency on
isAuthenticated
, the contents of the custom hook run repeatedly. - How do I separate the state for each instance of the custom hook? I am using useState in an attempt to maintain the
timeoutId
for each instance. Is this correct?
export const StatusIndicator = () => {
const isAuthenticated = useSelector((state) => state.auth.token);
const { data, q } = useGetStatusQuery();
usePolling({ callback: q }, [isAuthenticated]);
return (<div>{{data.status}}</div>);
};
export const usePolling = ({ callback }, dependencies = []) => {
const [instanceTimeoutId, setInstanceTimeoutId] = useState(null);
// I only want this run when `isAuthenticated` changes
useEffect(() => {
(async function continuation() {
clearTimeout(instanceTimeoutId);
await callback();
const { timeoutId, promise } = delay(interval);
setInstanceTimeoutId(timeoutId);
await promise;
continuation();
})();
return () => clearTimeout(instanceTimeoutId);
}, [...dependencies]);
};
2
Answers
As I’ve mentioned in my comment, you’re using
useState
for the timeout handle, so every time thecontinuation
function is called, it will callsetInstanceTimeoutId
, which will trigger a re-render ofusePolling
hook because of the state change.In fact, just a simple variable should be enough here. You could probably use
useRef
for the handle, but I think the simplelet timeoutHandle
should have no issues, since we’re only using the value in two places – withincontinuation
and in the hook cleanup. Both will just access whatever the current value is at the time.I would also have used
useRef
for storing mytimeoutId
as pointed our @NickParsons comment.To solve the first issue inside the second
useEffect
I’ve checked the condition that ifisAuthenticated
is true; immediately invoke async function. This function clears any existing timeout, calls the callback function and after that set a new timeout using the delay function. Now, The timeout id is stored ininstanceTimeoutId.current
ref. After the delay is passed, the continuation function is called again.