panzerdp / dmitripavlutin.com-comments

7 stars 0 forks source link

react-useeffect-infinite-loop/ #103

Open utterances-bot opened 3 years ago

utterances-bot commented 3 years ago

How to Solve the Infinite Loop of React.useEffect()

Be careful when using React.useEffect() hook because it can generate infinite loops.

https://dmitripavlutin.com/react-useeffect-infinite-loop/

acroyear commented 3 years ago

Another issue with loops can be caused by reading too much into the eslint configuration in your editor. One common thing I've found is that the eslint parser for react-hooks (at least when not using typescript) is greedy about dependencies used within the useEffect method. Global variables (aside from Window or objects specifically imported) are all flagged as warnings for dependencies. That includes the setter functions from useState.

But if you set a value and it re-renders, the setter is going to change (why, I do not know, but it does). So a simple effect loop like your example here,

    if (secret.value === 'secret') {
      setSecret(s => ({...s, countSecrets: s.countSecrets + 1}));
    }
  }, [secret.value]);

Flags a warning in eslint that setSecret is not in the dependency list. However, if you add it, you get the loop, because setSecret will change on every render. Likely internally the function is being bound again via bind(), and so even though it may be bound to the same object, it is still making a new function object. (My hypothesis - I have not looked at the react source codes).

Thus the annoying thing I've had to do is eslint-disable-line my dependencies array, which means I'm always running the risk of stale references if I'm not closely paying attention.

panzerdp commented 3 years ago

Thanks for the detailed description @acroyear.

Thus the annoying thing I've had to do is eslint-disable-line my dependencies array, which means I'm always running the risk of stale references if I'm not closely paying attention.

I rarely pay attention to warnings of the esling regarding dependencies because I understand how my side-effect works and dependencies it uses.

acroyear commented 3 years ago

Some corporation teams and some code reviewers have "no eslint warnings" built in to their pre-commit or pre-push hooks (easier to see potentially broken code in a build script). The false positive confuses new (to hooks) developers, since addressing it the way the hint implies means potentially introducing a bug.

Correcting myself a bit - it isn't necessarily useState setters directly that are a problem as much as using convenience functions that wrap those setters or reducers. Those functions will be recreated on each render and lead to the loop if added to the deps.

faridhuseynov commented 3 years ago

Hello Dmitri, thanks a lot for such a great article! I have found the reason for the infinite loop in my project, it was the array object (which I call products) being put as a dependency and at the same time updating state of this array once I get the result from database after fetching it. However, I was still unable to solve the issue, in the dependency I have put products.length (instead of previously putting just products) and I would expect the update & re-rendering as I add new product to the database. However, it simply doesn't happen. Could you please advise, what could be the reason for that or what am I doing wrong?

panzerdp commented 3 years ago

Hello Dmitri, thanks a lot for such a great article! I have found the reason for the infinite loop in my project, it was the array object (which I call products) being put as a dependency and at the same time updating state of this array once I get the result from database after fetching it. However, I was still unable to solve the issue, in the dependency I have put products.length (instead of previously putting just products) and I would expect the update & re-rendering as I add new product to the database. However, it simply doesn't happen. Could you please advise, what could be the reason for that or what am I doing wrong?

@faridhuseynov Can you share some code? It would be easier to understand.

faridhuseynov commented 3 years ago

Hello Dmitri, thanks a lot for such a great article! I have found the reason for the infinite loop in my project, it was the array object (which I call products) being put as a dependency and at the same time updating state of this array once I get the result from database after fetching it. However, I was still unable to solve the issue, in the dependency I have put products.length (instead of previously putting just products) and I would expect the update & re-rendering as I add new product to the database. However, it simply doesn't happen. Could you please advise, what could be the reason for that or what am I doing wrong?

@faridhuseynov Can you share some code? It would be easier to understand.

@panzerdp apologies, forgot to write back on my own comment. I have sorted it out, I have checked it in the tutorials, looks like fetching the data again and setting the updated list of items doesn't re-render it. Currently after adding new item in the firebase database, I'm adding also the item manually in the itemList that triggers re-rendering.

ROGERTERRILL commented 3 years ago

You always have such amazing write ups that help me so much. Thanks Dmitri.

panzerdp commented 3 years ago

You always have such amazing write ups that help me so much. Thanks Dmitri.

You're welcome @ROGERTERRILL.

AbreuY commented 2 years ago

Thanks for this article, i'm learning react js and i had this infinite loop problem. Your articles help me understand a little more react in general.

jaynaras commented 2 years ago

One more good article. Thanks again.

panzerdp commented 2 years ago

Thanks for this article, i'm learning react js and i had this infinite loop problem. Your articles help me understand a little more react in general.

You're welcome @AbreuY!

panzerdp commented 2 years ago

One more good article. Thanks again.

Glad you find it useful @jaynaras.

mikeebee commented 2 years ago

The part about avoiding objects as dependancies was a revelation and saved my bacon! Great article.

panzerdp commented 2 years ago

The part about avoiding objects as dependancies was a revelation and saved my bacon! Great article.

Glad my post has helped you @mikeebee!

pix303 commented 2 years ago

Thanks! very well explained tutorial. There are strategies to find out infinite loop? put every time console.log in useEffect handler? loss of app "responsiveness"?

iofirag commented 1 year ago

thanks!

panzerdp commented 1 year ago

You're welcome @iofirag!

acroyear commented 8 months ago

updating my comments above - more recent configurations of eslint react hook rules and deps-checking are 'smart' enough to figure out that the setter from useState doesn't need to be in the deps array and never changes, so those comments above can be ignored.

The convenience wrapper and useEffect can be solved by making the wrapper a useCallback.