Search code examples
javascriptreactjsreact-hooksuse-effectuse-state

React useEffect rendering more than once


I'm new in Hooks. I coded a project with componentDidMount.

Now I'm learning about hooks and rewriting this project with hooks. I want to fetch the data and print it on the console first.
However, it renders 3 times. It is probably because I used 2 setState in useEffect. However, in one of them I set the data to data array and in the other I keep the loading value for spinner control. How can I use useEffect like componentDidMount just one time to pull data and set my states?

When I write the console into useEffect, "React Hook useEffect has a missing dependency: 'data'." warning and returns an empty list.

Btw I deleted strictmode.

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

function App() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      const { data } = await axios.get("/data/data.json");
      setData(data);
      setLoading(false);
    };
    fetchData();
  }, []);
  console.log(data);
  return <div className="App">App</div>;
}

export default App;

enter image description here


Solution

  • I think having the data variable twice might be causing a conflict with the linter. You can rename your data coming from your API call to prevent the warning: "React Hook useEffect has a missing dependency: 'data'."

    Your component will re-render on each state update, but the useEffect will only run when your dependencies change. Since they aren't going to change, the API call only happens once.

    To prove it, you can move the console.log(result) in your useEffect and see it only logs once. However, make sure you call it on your result and not data, because the state won't be updated until the next render after calling setData.

    import React, { useState, useEffect } from "react";
    import axios from "axios";
    
    function App() {
      const [data, setData] = useState([]);
      const [loading, setLoading] = useState(false);
    
      useEffect(() => {
        const fetchData = async () => {
          const { data: result } = await axios.get("/data/data.json");
          setData(result);
          setLoading(false);
          console.log(result); // runs once
        };
        fetchData();
      }, [setData, setLoading]);
    
      console.log(data); // runs 3 times
      return <div className="App">App</div>;
    }
    
    export default App;