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
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 triggersuseEffect
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.
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:
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
insideuseEffect
and using that data as a dependency so on the execution ofuseEffect
,starWarsData
will be updated and on the update ofstarWarsData
,useEffect
will be executed creating an endless loop.So add an empty array as a dependency to execute the use effect only once.