Nicholas M. Salvatore

Back arrow

Understanding React's useEffect()

The primary reason to use an Effect is to synchronize data with an external system.

Per the React docs, "If you’re not trying to synchronize with some external system, you probably don't need an effect."

useEffect takes two arguments:

  1. A setup function

    • The logic inside of the setup function will run every time a change occurs in a dependency

    • A cleanup function may optionally be returned from inside the setup function which will execute every time the component un-mounts.

  2. Optionally, the dependencies, specified in an array

    • If no dependencies are specified, the setup function will run on every re-render.

    • If an empty array is specified as a dependency, the setup function will run only on the initial rendering of the component.

In the example below, every time the todos array is modified, useEffect synchronizes todos with a TODOS item in local storage. We know that this is the case because todos are specified in the dependencies array, which can be seen as the last argument in the useEffect call. As an unhelpful example of a cleanup function, useEffect in this example will call console.log('cleanup') whenever the component un-mounts prior to a re-render.

import { useEffect, useState } from 'react'

function App() {
    const [todos, setTodos] = useState(() => {
        return JSON.parse(localStorage.getItem('todos')) || []
    })
    
    useEffect(() => {
        localStorage.setItem('TODOS', JSON.stringify(todos))

        return () => console.log('cleanup')
    }, [todos])
}