Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding exhaustive dependencies into useEffect and useCallback causes infinite loop

So I thought I was getting the hang of these hooks, but the lint rule react-hooks/exhaustive-deps is tripping me up.

I have this method inside my Provider

const onScreenChange = useCallback(
(key, value) => {
  const newState = Object.assign({}, screenState, { [key]: value });
  localStorage.setItem('screens', JSON.stringify(newState));
  setScreenState(newState);
},
[]); // screenState

I'm allowing the rest of my app to access this by passing it into the value prop...

return <Provider value={{onScreenChange, ... }}>children</Provider>

And then I'm calling this method from a child component when the route changes

useEffect(() => {
   if (match.path === `/${screenKey}`) {
     onScreenChange(screenKey, 'external');
   }
}, [onScreenChange, match.path, screenKey]);

The above code works exactly how I want, and I can't see this causing any bugs. However eslint is telling me:

React Hook useCallback has a missing dependency: 'screenState'. Either include it or remove the dependency array

When I add screenState into the array, it causes an infinite loop as soon as the onScreenChange method is called.

It's pretty obvious why the loop is now happening, but how do I stop this and "follow the rules"?

Thanks in advance for any help!

like image 272
DanV Avatar asked Oct 15 '22 14:10

DanV


1 Answers

The warning from eslint seems to be correct. Since screenState value won't be updated correctly if the onScreenChange method is called multiple times. You must provide screenState as a depedency to useCallback

const onScreenChange = useCallback(
(key, value) => {
  const newState = Object.assign({}, screenState, { [key]: value });
  localStorage.setItem('screens', JSON.stringify(newState));
  setScreenState(newState);
},
[screenState]); 

The other way to write the same code without adding the deps is to make use of state updater callback pattern

const onScreenChange = useCallback(
(key, value) => {


  setScreenState(oldState => {
       const newState = Object.assign({}, oldState, { [key]: value });
       localStorage.setItem('screens', JSON.stringify(newState));
       return newState;
  });
},
[]); 

However you may choose to disable the deps warning if you are absolutely sure what you are trying to do is correct.

You may read below post for more inforamtion:

How do I fix missing dependency in React Hook useEffect

like image 73
Shubham Khatri Avatar answered Nov 11 '22 09:11

Shubham Khatri