React demystified: useEffect
Things that no one every told you about useEffect()
Hello there!
Welcome to another React demystified post.
Last week, we covered the useState()
hook. We took it to the fundamental level and asked ourselves the question - "What exactly is state?" ๐ค.
In this week's post, we will look at the useEffect()
hook. It is one of the complicated things that you need to deal with, while working on functional React components. But once you get the hang of it, there's no looking back.
useEffect()
is an escape hatch
Look at this seemingly harmless piece of code & tell me what you think the output is going to be:
export default function App() {
console.log('first');
useEffect(() => {
console.log('second');
});
console.log('third');
return (
<main>
<h1>Hello World</h1>
</main>
)
}
At first glance, it looks as though the output will be
first
second
third
But, but, but...
The actual output is
first
third
second
Let us understand why that is the case.
If you notice closely, useEffect
is a function, that takes in another function as its first argument. It then executes that function at a future point in time. Turns out, that it always calls the function after the React component is rendered and the DOM is updated. Even in the case of the first render. If you are not clear about how a React component gets translated into something on the DOM, check out this video:
The explanation
Now that we see useEffect()
as a kind of escape hatch than anything else, the above behavior makes more sense.
What happens is this:
console.log(first)
is executed and we see firstuseEffect
is executed and we supply a function to itconsole.log(third)
is executed and we see thirdReact component is rendered, DOM update happens
useEffect
executes the function we supplied & we see second
"So what is the point of the useEffect
hook if it comes into the picture after the DOM is updated?", you might ask.
Well, it is to trigger side effects (external).
According to the new React docs, it is quite clear what useEffect
is meant for:
So what does this sentence - "synchronize with external system" actually mean?
Well, simply put, it means all the things that are "outside" of the React component. That includes things like:
API calls
DB interactions
Subscribing to external events
So whenever you see any
useEffect
, now you know that it is an escape hatch to take you out of the normal flow of code and do things that deal with external systems. Although the code is placed inside of the function component (to take advantage of the closure property & access local variables), do not think of it as something that executes in the same flow.
The usage
With that understanding in place, let us look at a few use cases of useEffect
:
Side-effect on every render
This happens when we write code that looks something like the example above:
export default function App() {
useEffect(() => {
console.log('every render');
});
return (
<main>
<h1>Hello World</h1>
</main>
)
}
This defeats the purpose of useEffect
as it runs the side-effect every time the component is rendered.
side-effect once
If we pass an empty array as the second argument to useEffect, we are letting it know to run our function only once - right after the first render.
export default function App() {
useEffect(() => {
console.log('after first render');
}, []);
return (
<main>
<h1>Hello World</h1>
</main>
)
}
side-effect on dependency change
In this use-case, we are instructing useEffect
to run the function every time a dependency is changed (by specifying the dependency in the array). This might be useful if the side-effect is dependent on the dependency.
export default function App() {
useEffect(() => {
console.log('count changed');
}, [count]);
return (
<main>
<h1>Hello World</h1>
</main>
)
}
summary
This sketchnote summarises it well:
Hope that this article cleared up a lot of things like the need for something like useEffect()
in the React paradigm. Also, hope that the 3 use cases are clear and that you will feel more confident in your "effects" henceforth.
Cheers! ๐๐พ