UseEffect dependency array and object comparison!

Yogini Bende - Mar 30 '21 - - Dev Community

Hello folks!

If you are performing any side effects in your function, the Effect hook have to be there. This useEffect hook takes first parameter as a function to perform side effect and second parameter, a dependencies array. If you do not wish to perform side effects on every render (which is the case almost every time), you need to pass something to this dependency array or at least an empty array. This array will re-run useEffect, if the values inside it changes. This will work perfectly fine when the values passed in the dependency array are of type boolean, string or numbers. But it will have some gotchas when you are dealing with complex values such as objects or arrays.

Before diving into the solution of the problem, let’s first understand this problem in detail.

React always compares objects and arrays with their reference. This may affect the execution of useEffect in any of these two cases -
1- Object or array is exactly the same, but they are compared using different references.
2- Object or array have different values, but they are compared using the same reference.
In both the cases, useEffect hook will not perform correctly leading to the bugs in our application.

There are possibly two solutions to this. Let’s understand them in detail -

Create dependency on details

Consider an object with all the user details passed as a prop to the function. In your app, you want to perform the side effect only when the username of a user changes. So, in this case, the dependency becomes quite clear! Instead of passing whole user details objects to the useEffect, pass only the detail which matters. Something like this -


function UserProfile({userDetails}) {
    const [error, setError] =  useState(‘’);

    useEffect(() => {
        if(userDetails.username) {
            // Do something…!
}
}, [userDetails.username])
}

Enter fullscreen mode Exit fullscreen mode

This way, useEffect will compare the exact value and will re-run only when the username changes. This approach is suitable for small no of dependencies, but will not be clean and scalable if the array grows.

Memoizing the object

One more solution for this problem could be creating a new object every time. That way, we can always be sure that all changes are recorded and comparison is not happening on reference of that object. To better understand, let’s see the code -

function UserProfile({ userDetails }) {
    const [error, setError] = useState('');
    const [user, setUser] = useState({
        username: userDetails.username,
        email: userDetails.email,
        address: userDetails.address,
    });

    useEffect(() => {
        if (userDetails.username) {
            // Do something…!
        }
    }, [userDetails.username]);
}
Enter fullscreen mode Exit fullscreen mode

As you see in code, we created another copy object of the same object. Though this seems to solve the issue, there is a problem with this approach too. We all know creating objects in javascript is an expensive operation and we are trying to do that twice!! Apart from that, we are also duplicating the code which is again not a good practice to follow.

To solve all these problems, memoizing the object becomes a very simple and easy to maintain solution. Let’s see how -

Memoizing an object means we try to maintain a memory of the object. In better terms, we cache an object and maintain its one copy in our function. When the function re-renders, this same copy will be used in our function until any property of that object does not change. This way, we minimize the expensive operation of creating objects and also maintain an approach to catch the change.

For this memoizing, we use useMemo hook react. Let’s see the code -

function UserProfile({ userDetails }) {
    const [error, setError] = useState('');
    const { username, email, address } = userDetails;

    const user = useMemo(() => createUser({ username, email, address }), [
        username,
        email,
        address,
    ]);

    useEffect(() => {
        if (username) {
            // Do something…!
        }
    }, [user]);
}
Enter fullscreen mode Exit fullscreen mode

As in the above function, the createUser function will get called only when the username, email and address changes and new User object will be created.This way we make sure to compare correct copy of object in the dependency array and also optimize the unnecessary re-renders,

This is one of the tricky topics while working with useEffect as we tend to miss the fact that react will compare the reference of the object. Passing objects directly to useEffect will make the function buggy and we end up spending a lot of time figuring out what exactly is wrong!! (happened with me a lot!)

That’s it for this article and I hope it has helped you in some way! Please let me know how you solved the problem while passing objects to the dependency array? And of course, thoughts/comments/feedback on the article :)

You can also connect with me on Twitter or buy me a coffee if you like my articles.

Keep learning 🙌

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player