This hooks lets use side effects - things that happen outside the component and are called effects for short.

e.g.

  • fetching data
  • updating the DOM
  • timers

After React flushes the DOM, React runs your effect. This is done after every render including the initial render.

Resources

Triggers

The Effect hook will run when the component:

  • Is mounted (once) like using componentDidMount method.
  • Updates i.e. rerender (which can happen multiple times) like using componentDidUpdate method.
  • Unmounts (once) like using componentWillUnmount method.

Syntax

useEffect(effect)

Or

useEffect(effect, deps)

Docs:

@param effect — Imperative function that can return a cleanup function
@param deps — If present, effect will only activate if the values in the list change.

Basic

Pass deps as an empty array to only run on mount and unmount.

useEffect(effect, [])

Or so the effect only runs when that changes, pass your an array with your variable.

e.g.

useEffect(effect, [myValue])

Examples

Click

Here is an example where a callback function is run on state change, changing the DOM outside of the component, setting a value on document (or some element) uses the browser API.

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

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

Mount and unmount only

To prevent the function from running on updates and only on mount/unmount, specify an empty array as the second parameter.

useEffect(() => {
  console.log("Mounted or unmounted")
}, []);

The docs say that this is close to the mental model of componentDidMount and componentWillUnmount, but that “there are usually better solutions to avoid re-running effects too often”.

If you need to perform an action based on user-interaction in addition to the page load, you can do that outside the hook with an event.

// On mount.
useEffect(() => {
  loadPosts()
}, [])

// User-driven.
<button onClick={ () => loadPosts() }
  Fetch posts
</button>

Chat example

From Hooks Overview doc, here we subscribe to the chat notifications when the component mounts and then unsubscribe when unmounting (since there is no point to pull in data if the widget is not on the page anymore).

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {    
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);    
    
    return () => {   
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); 
    };
  });
  
  if (isOnline === null) {
    return 'Loading...';
  }
  
  return isOnline ? 'Online' : 'Offline';
}

Use multiple effects to separate concerns

Example from the docs.

function FriendStatusWithCounter(props) {
  const [count, _setCount] = useState(0);
  const [_isOnline, setIsOnline] = useState(null);
  
  useEffect(() => { 
      document.title = `You clicked ${count} times` 
    }
  );

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  // ...
}

Add and display post

The second parameter can be useful if you let the user save content to your database like create a post and you want to pull the latest posts immediately to display it.

useEffect(() => {
  getPosts()
}, [posts]) 

However, this can result in an infinite loop - if you add a log statement inside you’ll the effect run continously.

An article recommended using the count instead.

const [postLength, setPostLength] = useState(0) 
useEffect(() => {
    getPosts()
}, [postLength]) 

Comparison of Effect hook vs Class component

  • Functional component with effects to run on mount and update by just using useEffect.
      import React, { useState, useEffect } from 'react';
    
      function Example() {
        const [count, setCount] = useState(0);
    
        useEffect(() => {
          document.title = `You clicked ${count} times`;
        });
    
        return (
          <div>
            <p>You clicked { count } times</p>
              
            <button onClick={() => setCount(count + 1)}>
              Click me
            </button>
          </div>
        );
      }
    
  • Class component with logic to run on mount and update in separate methods.
      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`;
        }
    
        render() {
          return (
            <div>
              <p>You clicked { this.state.count } times</p>
                
              <button onClick={() => this.setState({ count: this.state.count + 1 })}>
                Click me
              </button>
            </div>
          );
        }
      }