Safely setState on a Mounted React Component through the useEffect Hook

Kent C. Dodds
InstructorKent C. Dodds

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 2 years ago

In the class version of this component, we had a method called safeSetState which would check whether the component was still mounted before trying to call setState. This is because our graphql client library is unable to cancel in-flight requests. Let's make that same kind of thing work by tracking the mounted state of our component using the useRef and useEffect hooks.

Instructor: [00:00] One other thing that our previous class component could do that our new component does not do is as this query is out in flight, there is a chance that our query component could be unmounted. That's why we had this safe setstate which would keep track of the is mounted state. If our component is mounted, then we'll go ahead and run setstate, otherwise we won't.

[00:21] We said that with mounted is false and our component will unmount, and mounted is true, and our component did mount. We have to do this because the client that we're using does not support canceling requests. If it did, then that would be the proper solution to this problem. Because it doesn't, we're going to see problems like this.

[00:40] Let's go ahead and we'll open up our developer tools. I'm going to change this to a slow 3G network, and then I'm going to go to can see dots. As it's loading our data for can see dots, I'm going to go back, that post request finishes, and we're going to get this can't perform react state update on unmounted component.

[00:59] Let's go ahead and solve this problem by implementing something similar to our safe setstate that we had before in our class component. I'm going to go back here, and we're going to create a mounted ref. Let's use ref is false. And then we use effect, and mounted ref.current will be true.

[01:21] When this effect is run, then our component has mounted, and then we're going to return a cleanup function. Here we'll say mounted ref.current equals false. When we clean up from this effect, our mounted ref.current will be false.

[01:36] Now, we only want this to run once when our component is mounted and then clean up when a component is unmounted. We're going to pass it inputs, and there will be no inputs, and therefore, our inputs will never change, meaning that this callback function will only be called one time, when the component is mounted in the first place.

[01:54] Then our cleanup will be called inversely when the component is unmounted. With that we get the same effect that we had before with our _ is mounted property on our class. Let's go ahead, and I'm going to make a new variable. I'm going to make a safe setstate, and that's going to take any number of RDS, and it's going to say mounted ref.current. If it is, then we'll setstate with our RDS.

[02:23] Perfect. Then we'll use this safe setstate in places where it could potentially have been unmounted, so both of these. We'll save that. Let's go back to being online, so refresh and then I'll switch us to slow. We'll do can see dots again. It's loading our application. We're code splitting here.

[02:43] Now, we're loading the data. I'll go back and our graphQL request finished, but we didn't get the warning that time, because we didn't try to setstate on a component that has been unmounted.

[02:53] In review, to make this work, we had to track the mounted state of our component with this mounted ref, and then we created the safe setstate method which would check whether that component was mounted, and if it is, then we'll call the setstate method.

[03:07] Again, this is generally not the way that you solve this problem. The better way to solve this problem would be to cancel this request, so that this promised change does not continue. That is not supported by our client library, so we're going to go ahead and track the mounted state of our component this way.

Gonzalo Pozzo
Gonzalo Pozzo
~ 4 years ago

What is the difference of storing the old value using useRef instead of using a useState?

Kent C. Dodds
Kent C. Doddsinstructor
~ 4 years ago

When you call the state updater from useState, you force a re-render. When the value of a ref is updated, no re-render happens. Use the hooks accordingly.

V School
V School
~ 4 years ago

Okay, thanks Kent