简体   繁体   中英

useEffect in React runs in an infinite loop, when an array is passed as a dependency

When the following code is run in a React component, it causes an endless loop. Why does passing an array as a dependency to useEffect (in this case, args ), cause that to happen? How can I stop the endless loop from happening? The code should only run once.

I've read that I can use [args.length] to stop this from happening, but then the ESLint rule react-hooks/exhaustive-deps throws an error, so I want to avoid doing it that way.

import React, { useEffect, useState } from 'react';

export default function Home() {
  const args = ['a'];
  const [value, setValue] = useState(['b']);

  useEffect(() => {
    setValue(['c']);
  }, [args]);

  console.log('value', value);
}

Notice in the code above, that I don't even use or modify args at all, in the useEffect callback.

If I change the value of args to a string, like const args = 'a' , then there is no endless loop. So the problem seems to occur when the dependency is an array.

The problem is that any change to args will cause your effect to run. At which point you setValue(args) so args changes again. This will cause your effect to run again... hence an infinite loop.

If you set args to the same string it was before it will not be registered as a change if they match per the way react detects changes. This will not work for an array or an object. {test: 'a'} !== {test: 'a'} and ['a'] !== ['a'] but 'a' === 'a' and 1 === 1 .

I also ran into your problem. I ended up disabling the react-hooks/exhaustive-deps rule. But I have more data for you. I struggled to try to use this rule for a while.

This post I made is very similar to your question. The end result is you should use setState as a function (ie. setState(oldState => oldState) from your example) at which point the old state doesn't need to be a dependency or useReducer which also produces the same effect.

However, neither of these are likely pure with what you may be trying to do which creates more problems as seen in this post I made later. If they are pure, you are fine.

The 4th solution on the 1st post describes how you can aggressively avoid this problem, but ultimately I decided to use the react-hooks/exhaustive-deps rule and disable it line by line when I intentionally delete dependencies I don't want. I don't agree it should it be a rule after all this.

The problem you are trying to do is balance React's requirement that useState and useReducer are pure with the requirement for exhaustive deps. I think this leads to a lot of work to make these requirements happy over actual productivity.

Using useRef fixed this issue for me:

const args = useRef(['a']);
const [value, setValue] = useState(['b']);

useEffect(() => {
  setValue(['c']);
}, [args]);

console.log('value', value);

And the value of args can be used within the useEffect callback, with args.current .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM