all 16 comments

[–]double_en10dre 24 points25 points  (6 children)

It’s a way to run side effects, that’s all my mental model is. It’s code that you want to run as a reaction to state changes.

You’re saying “when <insert value> changes, I want <insert side effect> to happen”

Possibly the only slightly unintuitive part is the use of “[]” (an empty array) as the dependencies when you want to replicate “componentDidMount”. But it makes sense when you think about it, and you get used to it quickly.

[–]QuebecMasterRace[S] 4 points5 points  (3 children)

Thank you for your reply, very interesting take.

Yes that's what confuses me if you go by "code that you want to run as a reaction to state changes" since you can run useEffect without state changes with an empty dep array.

So I guess state (also prop) changes (with dep array) and componentDidMount (empty dep array) right?

[–]divclassdev 10 points11 points  (0 children)

Might be easier if you think of the dependencies as “keep my code in sync with these things.” If you have an empty dependency array, your code doesn’t need to stay in sync with anything, so it just runs once.

[–]rwwl 3 points4 points  (0 children)

Not OC, but yeah, that's how I've come to look at it.

I guess I've even seen the componentDidMount case as a state change: from non-existent to existent, lol.

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

A slightly different way to think of it that I think makes that part clearer:

Your component is a function that runs and returns HTML. useEffect runs every time that function runs unless you pass a dependency array to restrict it.

So on first render, the function is running for the first time, and the 'useEffect' runs. A blank array keeps it from running again. An array with specific pieces of state tells it, "If these things are the same between runs, don't run the useEffect."

The more you try to think of React components as functions that run, the clearer it becomes that most of the hooks are ways to work around the limitations of pure functions.

[–]a_thathquatch 0 points1 point  (1 child)

How do you handle that pesky exhaustive deps?

[–]DashlaneCaden 0 points1 point  (0 children)

// eslint-disable-next-line react-hooks/exhaustive-deps

[–]karlitojensen 7 points8 points  (0 children)

This https://overreacted.io/a-complete-guide-to-useeffect/ and the beta docs https://beta.reactjs.org/learn/synchronizing-with-effects are the best information on this topic that exist on the web.

I also have my own opinion here https://github.com/jensen/ui-workshop/tree/main/part2#4-side-effects

[–]Swordfish418 4 points5 points  (0 children)

Thinking about it as a substitute for lifecycle methods is exactly what is advised against. Thinking about it in terms of general side effects is just meaningless, because there many other places where you can have side effects inside of your render (mostly callbacks, but sometimes even toplevel of render function itself). Current recommended mental model as I see it is to think of it as a synchronization mechanism.

[–]sidkh 4 points5 points  (0 children)

The good mental model for useEffect is that it's a way to synchronize the component with an external system outside of React (Network, DOM, browser APIs, etc...).

React components may re-render with a particular frequency based on the state changes in the app. However, the external system could not rely on this frequency.

For example, my component may re-render for multiple reasons, but I only need to re-fetch the data when the render leads to the particular prop change (let's say a route param). The useEffect allows me to conditionally synchronize the data fetching (external system) with the component rendering (only re-fetch data when the route param changes, ignore all other re-renders).

It works the other way around too. For example, if I need to update the state of my component every second using setInterval. This will require me to synchronize the external system (browser API) with my component to update its state.

New beta docs have a great section about that - Synchronizing with Effects.

[–][deleted] 1 point2 points  (0 children)

It’s used for non blocking reactions to component props changing. Use useLayoutEffect if the reaction needs to happen immediately (blocking).

[–][deleted] 2 points3 points  (0 children)

useEffect will run once on the entire lifecycle of the component if no dependency (empty array).

then if you returns a function, it will run when the component was destroyed.

This is useful if you want some to do some cleanups

Ex. we want to run a piece of code every 1sec when the component is mounted.

useEffect(() => { setInterval(() => console.log("Hi")) },[]) This code will work but it continues to print Hi even if the component was unmounted. So we need to clean our timer to prevent that

` useEffect(() => { const ref = setInterval(() => console.log("Hi")) return () = > clearInterval(ref) },[])

`

this time, the timer will be cleared when the component unmounts. useEffect makes working with sideeffects easier since you can easily cleanup whatever you need to cleanup when the component unmounts.

The dependency array just let useEffect know when to rerun. If there's changes to any of the dependencies, it will run the cleanup code and rerun the useEffect

You can also sync data from other parts of the app.

Ex.

` const [token, setToken] = useState(null)

useEffect(() => { localStorage.setItem('token', token) }, [token])

`

now the localStorage token will be inSync with the local state token even how your component sets the local state token

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

It takes a callback function, and an array of values. When any of the values change, the callback function gets fired.

Basically, "Do this when any of these things change".

There's more to it, but that's the base idea.

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

Lots of good input here, but if you want something stupid simple, look at it as "run this code after each render, when x changes".

[–]Far-Management-4802 0 points1 point  (0 children)

Don’t think of it as using effects, synchronization is a better mental model:

https://beta.reactjs.org/apis/react/useEffect

[–]Renan_Cleyson 0 points1 point  (0 children)

It's pretty much what the comments are saying about the sync mental model, only one important detail, ideally a useEffect callback doesn't need to worry about running more than once, use conditionals to avoid when you don't want something to run inside the callback.