useEffect hook

React useEffect hook and how to use it

React’s useEffect hook is a powerful tool for managing the side effects of your components. This hook allows you to run your code whenever watched value has changed, and can be used for tasks such as fetching data, setting up subscriptions, and manipulating the DOM. In this post, we will explore how to use the useEffect hook effectively and how to avoid some common pitfalls.

How and when use the useEffect hook?

If you are new to the topic, I have previously described what a React hook is in another post [1], so I recommend checking that out before continuing.

First, let’s understand the basic structure of the useEffect hook. This hook takes two arguments: a callback function that is fired as an effect, and a dependencies array that lists the values the effect depends on. The callback function will be executed after the component has rendered for the first time and the dependencies array is used to determine when the effect should be re-run.

For example, let’s say you have a component that fetches data from an API every time a certain prop changes. You could use useEffect to accomplish this, like so:

import { useEffect, useState } from 'react';

function MyComponent({ id }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    // fetch some data and assign it to 'data' state variable 
    fetch(`https://my-api.com/data/${id}`)
      .then(response => response.json())
      .then(data => setData(data));
  // we passed id to dependencies array, so whenever id changes, above code will be run
  }, [id]);

  // Render the component using the data
}

In this example, the effect is only run when the id prop changes. If the id prop stays the same, the effect will not be rerun, and the component will keep displaying the same data.

There is also one more important thing about this hook: callback function can return the cleanup function. This function will be invoked before the component is unmounted, and before the effect is run again. It is useful especially when you are using RxJS subscriptions because you can clean up your subscription here:

import { useEffect, useState } from 'react';
import { fromEvent } from 'rxjs';

function MyComponent() {
  const [message, setMessage] = useState('Click the button');
  const buttonRef = useRef(null);

  useEffect(() => {
    const buttonClick$ = fromEvent(buttonRef.current, 'click');

    const subscription = buttonClick$.subscribe(() => {
      setMessage('Button was clicked!');
    });

    return () => {
      subscription.unsubscribe();
    }
  }, []);

  return (
    <div>
      <button ref={buttonRef}>Click me</button>
      <div>{message}</div>
    </div>
  );
}

The above example creates an observable stream of button click events using the fromEvent operator, then uses the subscribe method to update the message state when the button is clicked. The callback function uses unsubscribe method to clean up the subscription when the component unmounts. The dependency array is empty here because we want to run this function only once, at the first component render, and clean it up when the component unmounts.

It is important to be mindful of the dependencies array when using useEffect, because if the array is not specified correctly, it can lead to unexpected behavior and performance issues.

Common useEffect mistakes

Now that we’ve covered how to use the useEffect hook, let’s discuss some common pitfalls to avoid.

#1 not specifying dependencies array

A common mistake is not specifying the dependencies array at all. If the dependencies array is not specified, the effect will be run on every render, which can lead to poor performance and unexpected behavior.

import { useState, useEffect } from "react";

const App = () => {

  useEffect(() => {
    doSomething();
  
    // we've forgot about dependencies array, so this effect will be run on every component render!
  });

  return <div>App</div>;
};

#2 Wrong dependencies

React compares changing of dependencies in the same way how you do comparisons in JavaScript. This means that if you compare objects and arrays, then their references will be compared. If you remember about it, then you can use it as an advantage to limit the number of effect reruns, by passing the specific field of an object to the dependency array:

import { useState, useEffect } from "react";

const App = ({ user }) => {

  useEffect(() => {
    doSomething();
  // run this effect only when name changes
  }, [user.name]);

  return <div>App</div>;
};

#3 Infinite loops

Another common mistake is providing a dependencies array with a variable that is changed in effect function:

import { useState, useEffect } from "react";

const App = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
     // counter is incremented
     setCounter(counter + 1);
     // counter value has changed after incrementation, so effect will be run again and again... 
  }, [counter]);

  return <div>App</div>;
};

Conclusion

In conclusion, the useEffect hook is a powerful tool for managing the side effects of your components. By understanding how to use the hook effectively and avoiding common pitfalls, you can build efficient and maintainable applications with React.

List of references

3 thoughts on “React useEffect hook and how to use it”

  1. Pingback: React useMemo hook and how to use it - Quality Coders

  2. Pingback: React useState hook and how to use it - Quality Coders

  3. Pingback: useCallback Hook in React: how and when you should use it? - Quality Coders

Leave a Comment

Your email address will not be published. Required fields are marked *