skip to Main Content

So here’s the code I’m confused about:

import React from "react"

export default function App() {
    const [starWarsData, setStarWarsData] = React.useState({})
 
    console.log(starWarsData)
    React.useEffect(function() {
        fetch("https://swapi.dev/api/people/1")
            .then(res => res.json())
            .then(data => setStarWarsData(data))
    }, [starWarsData])
    
    return (
        <div>
            <pre>{JSON.stringify(starWarsData, null, 2)}</pre>
        </div>
    )
}

This code will constantly be re-rendering the component in an infinite loop, but I dont understand why that is. In the useEffect function, it uses a fetch request for some data about Star Wars and will execute setStarWarsData on that data.

My understanding of this code is that when it first renders the component, it will then call the useEffect function (as always). The useEffect function will then change the state of the variable starWarsData which will then re-render the whole component however according to my flawed logic, the useEffect function shouldnt be executed after this current render of the component as the variable starWarsData is the same between the previous render and this render. If you put a console.log(starWarsData), the starWarsData variable will have the same value between the previous render of the component and the current render of the component, that is both will be objects that have the same content inside the objects.

Why is this logic wrong? Any help is appreciated 🙂

3

Answers


  1. The useEffect has two parameters, the first parameter is the function you want it to call.

    The second parameter is an array to watch and call your function whenever it change (in addition to when the component is mounted first).

    Your passing the state starWarsData in the array and inside of your function setting this state, the state change triggers useEffect to call your function again, creating an infinite loop!

    Pass an empty array if you want the function to get called only on component mount.

    React.useEffect(function() {
        fetch("https://swapi.dev/api/people/1")
            .then(res => res.json())
            .then(data => setStarWarsData(data))
    }, [])
    
    Login or Signup to reply.
  2. In addition to the answer above, you can also compare if the object is matching or not before updating the state so it doesn’t necessarily re-render.

    You can check the object change by JSON.stringify.

    To note here, there could be times when the structure of the data might not be in the same order in JSON and you might need to tweak it, but that’s least of our concern here.

    Your hook will look something like this:

    React.useEffect(function() {
        fetch("https://swapi.dev/api/people/1")
            .then(res => res.json())
            .then(data => {
                if(JSON.stringify(data) === JSON.stringify(starWarsData) 
                    {
                      setStarWarsData(data))
                    }
                }
             },
    [starWarsData])
    
    Login or Signup to reply.
  3. Here the problem is your dependency on the useEffect hook.

    useEffect hook will be executed once on the component mount, and your dependency will be changed each time.

    You are updating starWarsData inside useEffect and using that data as a dependency so on the execution of useEffect, starWarsData will be updated and on the update of starWarsData, useEffect will be executed creating an endless loop.

    So add an empty array as a dependency to execute the use effect only once.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search