React.useEffect: using it effectively

React.useEffect: using it effectively

This is a beginner-friendly post explaining the use of useEffect hook in React.

There are three phases of a React component,

  1. Mounting: When the component has rendered for the first time
  2. Updating: When the component has re-rendered due to a state change
  3. Unmounting: When the component is about to be removed from the DOM.

A React web application might need to perform some task at some or each of these phases. In class components you can perform those tasks in "componentDidMount()", "componentDidUpdate()" and "componentWillUnmount()" lifecycle methods. Let us see how do we achieve that using useEffect in functional components.

useEffect

    useEffect(()=> {
        //This is a callback function which is called by useEffect
    }, [dependencies]);

As shown above, the useEffect takes two arguments, first is the callback function which will have the code that needs to be executed on the call of useEffect and second is the dependency array which contains state or props on which the callback is dependent.

Mount Phase

A simple use-case would be updating the title of the document. The title of the document is usually not supposed to change throughout the component lifecycle. Following code will help us achieve this functionality.

    useEffect(()=> {
        window.document.title="Home";
    }, []);

In the above code, we set the title in the callback and leave the dependency array as empty, which indicates that this callback is supposed to be called only once as it is not dependent on any state or prop.

Update Phase

If we want to fetch data from an API based on an input, we store that input in state and pass that state as a dependency to the useEffect.

    const [input, setInput] = useState("");

    const fetchData = async () => {
        const result = await axios.get(`https://someurl.com/${input}`);
        .
        .
        .
    }

    useEffect(()=> {
        fetchData();
    }, [input]);

With the above code the fetchData will be called whenever input state changes.

Here, make note of two important gotchas

  1. The callback passed to useEffect should not return a promise. Hence we make the API call in the fetchData function and call it inside the callback.
  2. This callback will be called even in the Mount phase, you can stop that using a boolean useRef - initialMountRef which is initialized true, and when the component mounts set it to false in the useEffect. And return from the useEffect callback if the initialMountRef is true.

Unmount Phase

We do our cleanup tasks in this phase so that we don't have any memory leaks.

    useEffect(()=> {
        document.addEventListener("keypress", handleKeyPress);

        return () => {
             // this callback will execute when component is about to unmount
            document.removeEventListener("keypress", handleKeyPress);
        }
    }, [input]);

In the above code, we add an event listener on keypress event when the component mounts and then we return a callback in the useEffect which removes the event listener and which will be called just before the component unmounts.

So these are the different ways the useEffect hook can be utilized in a functional component to achieve tasks at all three phases of the React component.