Handle Deep Object Comparison in React's useEffect hook with the useRef Hook

Kent C. Dodds
InstructorKent C. Dodds
Share this video with your friends

Social Share Links

Send Tweet
Published 6 years ago
Updated 4 years ago

The second argument to React's useEffect hook is an array of dependencies for your useEffect callback. When any value in that array changes, the effect callback is re-run. But the variables object we're passing to that array is created during render, so our effect will be re-run every render even if the shape of the object is the same. So let's solve this by doing our own equality check from within the effect callback.

Instructor: [00:00] With our old query component, we were actually using this is equal from Lodash when we had this componentDidUpdate to compare the previous query and the previous variables with the new query and the new variables. We are doing that because the variables can actually be an object.

[00:16] As we can see here, the variables is an object that is going to be created new every single time. If we look at our useEffect, we are comparing the query and the variables. React is doing that for us, but it's not going to do a shallow comparison.

[00:32] It's actually going to do a direct comparison. It's going to say, previous variables, which it is tracking itself, and it's going to check that against variables. If that is not true, then it's going to rerun our callback.

[00:45] That's not what we want. This is actually always going to return false, because the way that people are using our component is, they're passing this variables object so every single time it is a brand new object. Every single time, our useEffect callback is going to be called.

[01:01] We need to do a little bit of extra work to make sure that that doesn't happen. What I'm going to do is, I'm going to bring back import is equal from Lodash is equal. Then inside of my useEffect hook, I'm basically going to simulate the same kind of thing that I was doing in here, which is not typically what we need to do.

[01:22] In our situation, because we're comparing two objects, and we need to make sure that they are deep equal, and we need to do a deep equality check on these, we're going to have to do it this way.

[01:33] I'm going to say, if is equal. I need to get my previous inputs, and then my current inputs, which is query and variables. Then I'll actually get rid of this right here, so I'll have our effects callback called every time and we'll do our own check right here.

[01:51] If they are equal, then we'll return. Now we need to find some way to know what the previous inputs were. Every single time this query is run, those inputs are going to change and we need to keep a reference to what those previous inputs were. We need to keep a reference.

[02:05] Let's try use ref. Down here below our useEffect, we're going to say const previous inputs equals use ref, and we'll use effect. After every single time our component is rendered, we'll say, previous inputs.current equals query and variables. Awesome.

[02:26] Then we're going to use the previous input.current rather than just simply previous inputs, and we'll compare the previous inputs with the new query and variables. Doing that, we can be sure that our effect is called every single time, but that we don't actually run our set state calls and our client calls unless the previous inputs are different from the new inputs.

[02:48] In review, what we did to make this work was, we ensured that our effect is called every time by removing the inputs argument here. We created a ref to keep track of the previous inputs on every render. Then we compared the previous inputs with the new ones, and if they are changed, then we'll go ahead and run our client to rerun the query.