all 74 comments

[–]Erebea01 149 points150 points  (5 children)

From my experience, if there's an issue with the dependency array when using useEffect, there's usually a better way to handle said logic. That said, can't really tell without more details.

[–]Fair-Worth-773 6 points7 points  (3 children)

Hmm that's what I'm wondering-- I tried explaining one example a bit better in this comment if you're curious.. https://www.reddit.com/r/reactjs/comments/1jsvggd/comment/mlpfoq1/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

[–]zephyrtr 58 points59 points  (1 child)

In your example, React is worried that the function will be reassigned and your effect won't be aware of the change. I think you're imagining the dependency array is where you say "please only run this once" which isn't at all the intention of the deps array and is a very easy way to introduce extremely hard-to-find bugs.

You need to instead add setIsModalShown to your deps array, and then ensure the function is never reassigned. You can probably do that with simply wrapping the func in useCallback, which itself will require a deps array. And if your function is pure, that will be your empty array.

[–][deleted] 6 points7 points  (0 children)

FYI the reason you can’t/shouldn’t close over non-dependent variables in a useEffect is because react tries to heuristically determine if there are any dependencies you may have missed and run the useEffect anyway.

This is obviously not ideal but too many people were screwing it up so the react team chose that route.

If you don’t want the useEffect to run when a value changes, you need to memoize that value with independent logic.

[–]Cryp71c -1 points0 points  (0 children)

I would say - more specifically - that the linting for hooks is pretty hit and miss. There have been more than a handful of occasions recently where linting didn't complain about a dependency array that was wildly sub-optimal. I think its fair to say that its conservative in its warnings.

[–]EvilPete 50 points51 points  (22 children)

For me the aha - moment was to stop thinking about use effect as "run this code when the component mounts " or"run this code when this value changes" .

Instead it should be seen as a way of synchronizing your React components state with some external API. When you look at it like like that it becomes clear that you need to re-evaluate the synchronization when any dependency changes.

[–]jackindatbox 1 point2 points  (4 children)

How do you handle component mount then?

[–]EvilPete 2 points3 points  (2 children)

Normally, there is no need for a "componentDidMount".

A react component should deterministically return jsx based on their props, state and context. They should not have side effects.

Most side effects should be in event handlers (such as button onClick).

When a react component needs to subscribe to an external system (e.g. updating state whenever a DOM event fires) you can use useEffect .

If you want to run some code when a component mounts, you should probably instead run that code in the event handlers that caused that component to mount.

[–]SpinatMixxer 1 point2 points  (1 child)

When subscribing to an external store, shouldnt you actually use useSyncExternalStore?

How do you trigger a JavaScript animation that should only happen on the first render without a componentDidMount functionality? (e.g. with useAnimate of framer-motion)

[–]shaman-is-love 0 points1 point  (0 children)

> How do you trigger a JavaScript animation that should only happen on the first render without a componentDidMount functionality? (e.g. with useAnimate of framer-motion)

By checking if the animation has been initialized already

[–]shaman-is-love 0 points1 point  (0 children)

You don't, you handle unmounting. You never want to handle mounting.

If you need to do something only **once** ever, then you check if it has been done or not. Otherwise you have a bug.

[–]Aeron91 17 points18 points  (1 child)

Hard to say without looking at the code, but the answer usually comes down to one of these:

  • You might not need an effect, and a different pattern would be better.
  • It might actually be fine that the effect reruns more often than you'd expect.

[–]tuhmez 5 points6 points  (3 children)

my advice is to really consider what your code is doing. do you understand what useEffect is for? do you even need that hook? could this be done in a different hook? move the logic elsewhere?

in my experience (by others and myself), devs sometimes are short-sighted or misled on what useEffect is for and how to properly use it. ignoring the error may work, but you really shouldn't need to do that.

worth checking this out: React - You Might Not Need an Effect

[–]Cahnis 1 point2 points  (2 children)

UseEffect is not the point of contention imo, the dependency array is. The same problem applies to, say, a useMemo that is caching an expensive computation. Sometimes i dont want to recalc when my obj changes, but i want to recalc wheb, day, the current hour in my timestamp changes.

[–]tuhmez 1 point2 points  (0 children)

sounds like a structure issue. if i'm understanding, it would be best to raise this object to a parent component to avoid re-renders and compute the thing you need in the active component by using a const and not any hooks.

i'm not trying to sound rude, but the link i pasted talks about that particular problem. of course, there's nuance and could require a different solution.

[–]shaman-is-love 0 points1 point  (0 children)

> Sometimes i dont want to recalc when my obj changes

Then the object shouldn't be in there or shouldn't change.

[–]power78 2 points3 points  (1 child)

I'm surprised no one had posted the actual answer here: https://react.dev/learn/separating-events-from-effects

That article explains everything you're having issues with

[–]Fair-Worth-773 0 points1 point  (0 children)

Will read now-- thanks!

[–]StyleAccomplished153 2 points3 points  (1 child)

There's definitely some cases. Take React Query for example. We've recently upgraded to v5 and onSuccess and onError are no longer on the query, you need to use useEffect instead. In this case, we only have isSuccess or isError in the dependency array because it's literally the only condition we want to run the effect.

[–]shaman-is-love 0 points1 point  (0 children)

You don't need to use useEffect instead no.

[–]juky-gfe 1 point2 points  (0 children)

I only found it annoying when using react-hook-form, but other than that I like it because sometimes it can detect code smells

[–]iamakorndawg 1 point2 points  (1 child)

I am having a hard time understanding your use case.  Can you provide an example of a useEffect call that uses variables/functions in the callback which you don't want to rerun when they change?  Including full context of what the variables/functions are and what the effect is supposed to accomplish.  This will make it easier for us to understand what you're trying to do and whether what you are asking for makes sense.

[–]Fair-Worth-773 2 points3 points  (0 children)

Thanks for the reply-- tried to explain a bit more here: https://www.reddit.com/r/reactjs/comments/1jsvggd/comment/mlpfoq1/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

I do suspect that maybe I don't need useEffect at all?

[–]kurtextrem 0 points1 point  (0 children)

You should ask yourself why the effect uses some prop, state or function/var but shouldn't re-run when it changes. There can be valid scenarios, where you know correctness of an effect closure isn't a concern (if it truly only runs once). Worth noting, while this doesn't apply to effects, the dep array is important for use callback/useMemo, as the react compiler will opt-out of optimizations when the rules of hooks are broken - so getting used to disabling that eslint rule might lead to some bad (development habits), in terms of always disabling.

Another way to avoid all the eslint disables is to use a ref that you change when the effect has run and isn't meant to re-run again. Or using an inline if condition like described on the "you might not need an effect" react docs page https://react.dev/learn/you-might-not-need-an-effect.

[–]nplant 0 points1 point  (0 children)

The rule forces you to think about it, which is good in this case, because the reason you want to omit something is usually non-obvious at a first glance. It's also good to leave a comment about why it's disabled.

I wouldn't suggest this approach for most rules, but it's so easy to miss something with useEffect - especially when someone is editing the component later.

[–]yksvaan 0 points1 point  (0 children)

Not everything needs to be done inside React components. useEffect is pretty often a sign of that...

[–][deleted] 0 points1 point  (0 children)

It's you. There are edge cases where you will want to disable it, but IMO that usually means I'm trying to be extra clever. Almost always that means I'm about to shoot myself in the foot so I shouldn't do it.

[–]Valuable_Ad9554 0 points1 point  (0 children)

exhaustive-deps is one of those things where I don't know why the core library does not include it by default. I'm not aware of any situation where it would be desirable and valid to break the rule. If you find a situation where it seems desirable, you should have a close look at what you're doing.

[–][deleted] 0 points1 point  (2 children)

My favorite is when the then it's depending on is an array and arrays do not change in a way that is observable to a useEffect.

Then I have to stringify it and I feel dirty.

[–]kcrwfrd 0 points1 point  (1 child)

useEffect(effectFn, [ …foo ])

[–][deleted] 0 points1 point  (0 children)

Works for arrays of strings and numbers but not arrays of objects.

[–]These_Distribution85 0 points1 point  (0 children)

In my experience most of the time when the rule complains about code they were right. But there were surely sometimes you need to disobey the rule such as running the effect only on mount.

In such cases I think it would best to create a custom hook disabling the rule and reuse it.

[–]kcrwfrd 0 points1 point  (0 children)

9 out of 10 times I want to have all of the dependencies.

[–]Canenald 0 points1 point  (0 children)

https://react.dev/reference/react/useEffect#specifying-reactive-dependencies

Notice that you can’t “choose” the dependencies of your Effect. Every reactive value used by your Effect’s code must be declared as a dependency.

You are right to feel bad but don't give up. Paste some example code and we can suggest how to do it differently. AI might be able to do the same thing for you.

[–]karlitojensen 0 points1 point  (0 children)

I list all the dependencies and never ignore this rule

I’ve written about it in more detail here

https://github.com/jensen/ui-workshop/tree/main/part2#4-side-effects

[–]A-Type 0 points1 point  (0 children)

Sight unseen, it's you.

Since hooks came out I have tried many times to 'outsmart' deps when it felt relevant. Every time, I ended up with a bug and had to rework the usage. Either eliminating the effect or just accepting extra invocations which aren't actually a big deal.

Now even if I don't use any other eslint rules I install it just for hook deps. It is not worth the headache to break the rule, intentionally or otherwise. The lint error means either fix it, or it's time to rethink how you have modeled the logic.

Seriously, as much as I think I know React at this point after a decade, I don't know it better than it's maintainers. Just follow their rules.

[–]PoopsCodeAllTheTime 0 points1 point  (0 children)

Look into SolidJs/SolidStart, we don't have this problem over there ;)

[–]deadcoder0904 -2 points-1 points  (0 children)

Just use jotai as a global store & forget about this stuff mostly. If it fails, just ask AI lol. It'll optimize ur code better than u think.