Hello friends, let’s try to understand useEffect
and how it works, and talk about the most common useEffect problems. You might think that you
know how to use useEffect, but it's really important to understand the main concepts of this
hook. When does it run? How do dependencies work? What's the difference between primitive and
non-primitive dependencies? When we should use a clean-up function? I'm gonna cover all
those questions. So don't skip this video, it's gonna be really helpful
for you to understand the basics and you'll feel more confident when you
use useEffect in your next projects. Okay, as you can see we have a number state. In
the beginning, this number is 0. And we have a button that increases this number using a click
event. And we show this number in this span. I'll write here console.log, so whenever we update
our state, our component will be re-rendered. Like that. Actually, let's write
here how many times it renders. Okay. As you realize, at the beginning it renders
the component twice. But don't worry about that. It's just because of React.StrictMode in the
index file. If I disable it, as you can see it works as we expected. We are gonna talk about
this StrictMode and why it's happening soon. But before, I'll create a useEffect. And I'll
say again console.count. So we'll be able to see when and how many times it runs. And let's
write here any effect. It can be an API request, dom manipulation or whatever we want
to run after rendering the component. So let's change the title of the page for
example. I'm gonna write exactly the same thing. So, when I click on this button, as you can see,
it updates the title. And as you realize there is a really short delay between this number on
the page and this number on the title. That's because useEffect runs after rendering the
component. It returns all these elements here, and only after that runs this effect. Let
me demonstrate what actually happens here. Essentially we have 3 main elements.
Our component, React itself, and the browser. And in the first render,
the number is 0. The component says: Hey, React! You should render this HTML result. And
after rendering, don't forget to run this effect. And React says, Hey Browser! We have some changes
to update DOM. And finally, the browser takes those changes and shows them on the screen. And
after this rendering process, React finally runs the effect that we give. And the browser takes
those changes and shows them on the screen again. But there is a problem here
because this useEffect runs on every render. If I create a name state
here and update that name using this input, whenever I write something inside this
input, my component will be re-rendered. And as you can see the useEffect runs also. But
it's not supposed to run because it depends on only this number. This is where we should use
useEffect dependencies. After this function, I'll write an array and inside its dependencies.
In this case, it's gonna be this number. And it means, run this function at the beginning, and
after, run it again when only this value changes. And right now, as you can
see, it runs at the beginning, it's not gonna run again even if I re-render
the component unless I change this number. And sometimes we don't need any dependency
and we want to run an effect only once. Let's say "Title of the app". And we don't
need this anymore, I'll delete. It'll be an empty array. In this case, it'll run
only after the initial render. Like this. But there is something you need
to be really really careful about. And it's the type of dependency. Let me explain. As you can see we have two states and there
is an input which updates this name state and we show this user state here. Name is
an empty string, and selected is false. Let's create 2 buttons. First one will be
updating the user name, and the second one will be updating the selected boolean. Let's create
functions quickly and update object properties. If you don't know why we are updating them like that I highly recommend you to
watch the previous video. Okey, as you can see it works properly.
Right now I want to create a useEffect. And whenever we change this state, it's gonna
write "the state has changed, so useEffect runs". Let's try. It's the initial
render. I'll update this name. As you can see it works again because the
state has changed. I'll click here and selected is true right now, and useEffect
runs again. Everything works perfectly. If click again it's still true. But
as you realize useEffect runs again. But why is that happening? Let's click here. The
name is still john, and the selected is still true. Nothing has changed, my dependency is still
the same. Well, it's a really common problem, and it's hard to understand for beginners because
they are struggling to understand the difference between primitive and non-primitive data types.
But don't worry I'm gonna explain everything. Let's say there is a variable which is a string
." john". And another one. It's "john" also. And let's make some comparisons.
I'll say a equals b. And it's true. They both are john. a equals john. And of course,
they are the same. Basically, john equals john. Let's do the same thing for
numbers. c equals 1, d equals 1. And if I compare them it's gonna be
true. Is d equal to 1? Of course. And finally booleans. true equals true, false
equals false. It's exactly the same thing. So those 3 data types are primitives.
Strings, numbers and booleans are primitive. What about objects? Let's create quickly two
objects which are exactly the same. And if I compare them, as you can see it's false. It's
happening because even if they look the same, they refer to different points in the memory, so they
are two different objects with the same content. You can think like two shopping carts. They
look the same, they have the same products inside but their locations are different so
we can understand that they are two different carts. And if I refer to exactly the same
point, these two objects will be the same. I'm not creating a new one, I'm
just referring to the same location. Of course, I'm not gonna explain stacks,
heaps or that kind of data structure terms. But you should know that computers compare values
of variables when they hold a string, number, boolean, null, or undefined. But when it comes to
objects or arrays, they compare their references instead of their content. So we can compare arrays
in the same way. And it just works like an object. But don't misunderstand here. In React, yes we
are using dependency arrays. But React doesn't compare two different arrays like that. It
compares every single element in this array. Okay, if we give here variables with
primitive values. It's not a problem, but if it's an object or array we have to be
careful. So how we can solve this issue? Actually, there are different options. If you want to
you can create here a useMemo hook, and memoize what you need in the state. In this case, we
are gonna need a name and selected. And this user will be changed only if the name or selected
changes. So even if we have more properties here it's gonna depend on only these
two. Let's change this dependency. Okay. The name has changed but after
that, it's not gonna work again and again because the name is still the same. Let's make
this true. And after it'll not run it again. What else you can do?
Basically, instead of useMemo, you can write here every single primitive
variable like that. It depends on your use case. Another mistake is to incorrectly
update the states in a useEffect. Let's say we have a number state again. I'm gonna
write it here. What I want to do is to increase this number every 1 second. To do that I'll use
setInterval. Every 1 second it'll update the state like that. If you look here you'll see that
something is wrong. There is a weird glitch here. If I write here console log. And as you can see there is an infinite loop.
It's happening because every second the number is changing. And since the number is
our dependency it runs this function again and again. To solve this problem instead of
updating states using the state variable itself, you should use an updating function. I've already
explained this function in the previous video. And now, it runs only once
and it works almost perfectly. I say almost because it takes us to another
common problem. useEffect clean up functions. Let's add here any text and save the file. And
as you realize it just broke the counter here. Whenever I add something else, it gets worse.
That is because in every render we actually create another interval without cleaning the previous
one. As you can see we have many intervals running. Of course, to find out this issue, we
shouldn't test it by writing here something, and this is where React Strict Mode is so important.
But we are gonna talk about it at the end of the video. We can test it like that for now. To fix
this problem we should use a clean-up function. Let me explain how it works. It's pretty easy.
Let's say we have a toggle state and a button. When we click on this button it updates
the toggle. If it's false it makes it true, if it's true it makes it false again. So
basically it'll re-render the component on every click and it's gonna trigger this useEffect. Like that. In the first render, this effect
is gonna run anyway. But for the next renders, we can return a clean-up function that runs
before the actual effect in order to cancel what we did in the previous useEffect. You
are gonna understand better right now what I mean. Let's demonstrate with console.logs. In
the initial render, it'll say useEffect runs, and we can make something using toggle or
whatever. And when I click on this button, before running this effect again, it's gonna
run this clean-up function so we can clean the previous interval or event listener or whatever
we do inside this effect. I'll say "Wait!". And after cleaning "Okey done! You can run the
effect". Okay, let's try. It's the first render. And when I click, it runs the clean-up function
first, and only after that it runs the effect again. And it's extremely useful. It prevents
any memory leaking and makes your applications much faster. I'll give some use-cases and you'll
understand better. Let's get back to the previous example. As I said we, should clean this interval.
I'll create the clean-up function. And inside clearInterval function. Of course, we should pass
this interval in order to clear it. To do that I'll say const interval and I'm gonna pass it
here. And let's write something. Cleaning time. Okay. As you can see after every renders it
cleans the interval. So it doesn't occupy a place in the memory each time. And another
most common case is fetching data from an API. Let's say we have 2 pages. This page includes
just a link. And when we click on this link it shows the posts page and here we
are fetching data using a useEffect. There is a fake API and it includes 100 posts.
After fetching those posts, it updates this state. And finally, we are showing the title of
each post here. As you can see everything looks okey. But actually, there are some
possible problems. Right now I'll go ahead and turn on the slow mode. So we can observe
clearly. And let's write here the posts. Okey. I'm clicking and before fetching data I want
to turn back. And as you can see even if I'm in another component, it still fetches all these data
and updates the state. But it should be cancelled immediately as soon as we leave the component.
This is why we need a clean-up function here. It's a basic example, but for more complex cases with
more states, you'll have bigger problems. So you should come here and create a clean-up. And
I'll show you a really common strategy. Firstly I'll create here a variable. And it's gonna
be true when we fire this function. Basically, we are gonna subscribe to this API request. And
as long as it's true, we are gonna set the state. But when we want to cancel it's gonna be
false. This naming is not important, you can say anything. Like isCancelled. It's easier to
understand I think. At the beginning it's gonna be false. And in the clean-up function, it's gonna
be cancelled true. In this case, we'll update only if it's false. Let's try. Right now, everything
works perfectly, it doesn't update the state. Let me give you one more example. We have a user
component and it fetches a single user using the id in the URL. If it's users 1 it fetches the
first user, if it's 2 fetches the second user so on. Finally, we have the user information
here and 3 links in order to go to different user profiles. Basically, when we click them
the id will be changed and when the id changes useEffect will fire this function and update the
user. So if we don't use a clean-up function, let's see what's gonna happen. I'll turn the slow
mode on again. Now, I'll click on user2 and user3 right after that. And as you can see even if the
URL is user 3 it shows the second user first. That is because it's not cancelling and updating
the state in any case. User1, user2, user3. It shows us all of them and is pretty annoying.
Let's do the same thing. This time I'll use another common naming. unsubscribed false. If
we cancel it's gonna be true. And I'll write "cancelled". And if we are still subscribed,
which means unsubscribed is false, we'll update the user. You can do the same
thing for the catch block also. Let's see. Okey perfect it shows only one user. By the way, it's not the only way to cancel
requests. For beginners, it's totally okay, but if you want to go ahead and create
a more professional clean-up function, you can use javascript AbortController. It allows
us to intercept an API request so we can cancel it at any time. We are gonna use its signal method
and pass it in the fetch method as an option. Basically, when we want to cancel the request, we
are gonna send here a signal and the fetch method will be destroyed immediately. Of course, we are
gonna send this signal in the clean-up function. Controller and abort. Let's try. As you can see,
it throws an error. The user aborted a request. Perfect. Let's go one step further and write here
a catch method to handle this error. I'll write a condition. if the error name is Abort error which
identifies this error. I'll say request cancelled. If it's something else you can write it directly
or create an error state and update it here, whatever you want. I'll say todo: handle error. Let's try. I'm clicking. And
perfect. It's much better right now. And if you are using Axios instead of Javascript
fetch, there is something similar. Let's change it to axios first. It'll be a get method. I'll delete
this option we are gonna write something else. We don't need a JSON object. It's gonna
return a response and we'll use response.data. And I'll delete here and here. Okay. It's really
similar. We are gonna use Axios cancel token and source. Whenever we want to cancel the request
we are gonna use the cancel method. And I'll pass here the cancelToken like that. And finally,
Axios is gonna check whether the error comes from the cancellation or not. If it does, it'll
write here request cancelled. Btw those codes will be in the description below you can check
them whenever you need them. Okay. Let's try. And Perfect. Okay, before ending let's talk about React
strict mode. I'll write here "useEffect mounts" and "unmounts" or "useEffect runs",
"clean up runs" whatever you want. And as you can see it runs only once. But if
I take this Strict mode back, as you can see, this strange behaviour occurs. It just acts
like the component renders twice. But actually, it doesn't. After React 18, it became one of
the most controversial parts of the library. Some developers got crazy, they thought the new
version of React was broken, and they tried to find out some strange solutions. But the answers
to all questions are already on the official website. Firstly, there is nothing to worry about,
it happens only in development mode. So when you deploy your apps, it's gonna work as you expected.
Secondly, it's not a bug, it just makes you sure that there is no problem before the production.
There are some titles here that Strict mode helps. But since this is a useEffect tutorial, I'll
explain how it affects useEffect lifecycles. For better understanding, I'm gonna open up
the previous example. I'll comment this out again. And if you remember, even if we don't
use a clean-up function, it just works fine and there is no way to understand
if something is wrong or not. We just found out the problem by
adding here some additional elements. Let's take the Strict mode back. And right now, as you can see it
increases this number two by two. And I understand that something is wrong. I
couldn't complete the life cycle properly. And if I use the correct version
and clear the previous interval, Strict mode tests it for me and it works
perfectly. Basically, This mode is really important to find out bugs, especially in
useEffect. And I recommend you not to remove it or try to find some weird solutions. Just leave
it like that. Believe me, it's gonna help you. Okay, I think it's enough for today.
If you learned something new today, please like the video. And let me know which hook
you want to see in the next tutorial. Don't forget to follow lama dev's social media accounts. I
hope I'll see you in the next tutorial. Goodbye.