React Use Effect

React.useEffect()

This small create-react-app demo consists of two small components. The main App component and its child, UseEffectDemo.
The App component has state which we use to augment the 'url' property we pass to UseEffectDemo to fetch from the dummy api.

App Component

import React, { Component } from 'react';
import UseEffectDemo from './comps/UseEffectDemo'
import './App.css';

const testUrl = 'https://jsonplaceholder.typicode.com/todos/'

class App extends Component {

  state = {
    postId: 1
  }

  handleIncrementPost = () => {
    this.setState(prevState => {
      return {
        postId: prevState.postId + 1,
      }
    })
  }

  render() {
    const {postId} = this.state;
    return (
      <div className="App">
        <header className="App-header">
          <button
            style={{height: 100, width: 300, fontSize: 50}}
            onClick={this.handleIncrementPost}>
              Increment Id
          </button>
          <UseEffectDemo url={testUrl+postId}/>
        </header>
      </div>
    );
  }
}

export default App;

UseEffectDemo Component


import React, { useState, useEffect } from "react";
import axios from "axios";

export default ({ url }) => {
  const [data, setData] = useState(null);
  useEffect(
    () => {
      let mounted = true;

      const loadData = async () => {
        const { data } = await axios.get(url);
        console.log("data = ", data, "\n");
        if (mounted) {
          setData(data);
        }
      };

      loadData();

      return () => {
        mounted = false;
      };
    },
    [url]
  );

  if (!data) {
    return <span data-testid="loading"> Loading data...</span>;
  }

  console.log("data = ", data, "\n");

  return <h1>{data.title}</h1>;
};

Breaking down UseEffectDemo

useEffect() doesn't run on the initial render. We 'skip' directly to if(!data) and 'Loading' is displayed.
Next, after rendering, it calls useEffect(). Within useEffect(), loadData() is called, which fetches external data and updates the state with setData(data). This updates UseEffectDemo's state, triggering another render. During the second render, we have data, so we make it to the final return statement, and the title property of the fetched object is displayed.

useEffect()

useEffect() is to cause an 'effect' when the data (props, state) of the component changes. Anything that isn't directly related to rendering the view is a good candidate for being placed inside useEffect()

useEffect() takes two arguments

the first: A function to call after each render, aka anytime state or props get updated.

the second: it uses to determine weather or not run useEffect() again.
useEffect(() => {  }, [url])

If you pass an empty array as the second argument, [], useEffect() will only run once.

In this case study, we only want to run useEffect() when the url property changes. This will happen anytime the user presses the increment button.
useEffect(() => {
  // stuff to do on rerender
  return () =>{
    // cleanUp function to call after component has finished unmounting.
  }
}, [url])


If your first arguments function returns a function, that function will be run after unmounting, which can be used to clean up things like event listeners.

About 'mounted'

The mounted variable that is introduced is a bit of a hack. It exists so that we can use an if statement to check to make sure the component is mounted before we attempt to setData(data). Otherwise, we would be setting state on an unmounted component, which is a no-no.
arrow_back

Previous

Boiler Post

Next

Big O
arrow_forward