sajad torkamani

This post is all over the place – just like my understanding of useEffect 🙃

useEffect lets you synchronize side-effects with component state

To make your UI’s behavior predictable and to avoid subtle bugs, the React docs recommend you write your components as pure functions so that given the same props, they always return the same JSX.

But sometimes you need your components to respond to user actions or to interact with external systems. For example:

  • Display a modal when the user clicks a button.
  • Focus an input element when a component first mounts
  • Fetch data from a server when a component first mounts.
  • Setup subscriptions to external services (e.g., Firebase’s Cloud Firestore or a GraphQL server)

You usually place these side effects inside event handlers. Event handlers don’t run during the component’s rendering phase, so they don’t need to be pure.

But sometimes you can’t find a suitable event handler for a side-effect. For example, you can display a modal on the click of a button like so:

<Button onClick={() => { showModal('blah') }>Show modal</Button>

But it’s not obvious what event handler you could use if you wanted to fetch external data when a component first mounts. When you’ve exhausted all other options, you can use useEffect as a last resort.

You can use useEffect to tell React to execute a side-effect whenever a piece of component state changes – this can be a state created with useState, a prop, or a value computed from state or prop.

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]); // Execute effect every time `count` changes
}

When does useEffect run in the component lifecycle?

After the first render

When the component is initially mounted on the DOM and has finished rendering. You will have access to the initial state and props inside your effect. If you don’t want to execute the effect on the first render, see this post.

After every subsequent render

Every time the component has finished re-rendering, your effect will be executed. If you’ve specified any dependencies, it will run only if one of the dependencies changed since the last render.

useEffect doesn’t block the browser painting process

Unlike componentDidMount or componentDidUpdate, effects scheduled with useEffect are deferred and executed after the browser has finished its layout and painting process. If you need to perform an effect before the next browser paint, you can use the useLayoutEffect instead.

Although deferred until after the browser has painted, useEffect is guaranteed to fire before the component is re-rendered. React will always finish a previous effect before starting a new re-render (need a working example of this).

useEffect vs class component life cycle methods

useEffect is a replacement for the following lifecycle methods in a class component:

In class components, you place side-effects in the componentDidMount and componentDidUpdate methods. You clean up the side-effects in componentWillUnmount.

So, the following class component:

import React from 'react'

class Example extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`
  }

  componentWillUnmount() {
    // Do some clean up
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    )
  }
}

can be written as a functional component that uses useEffect:

import React, { useState, useEffect } from 'react'

function Example() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    document.title = `You clicked ${count} times`

    return () => {
      // Do some clean up
    }
  })

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  )
}

How to use

Other notes

The effect function will be different on every render

Every time a component is rendered (either initially or subsequently), the effect function will be a different function in the sense that it will be a new entity in the JavaScript engine’s memory heap.

Sources

Tagged: React