Home > OS >  re-render twice not same state when use fetch with useEffect in Reactjs
re-render twice not same state when use fetch with useEffect in Reactjs

Time:03-12

I am having the following problem. This is my Code

const [championData, setChampionData] = useState({})
  const championUrl = `http://ddragon.leagueoflegends.com/cdn/12.5.1/data/en_US/champion/${champion}.json`
  useEffect(() => {
    fetch(championUrl)
      .then(response => response.json())
      .then((data) => {
        setChampionData(data.data[champion])

      })
  }, []);

  console.log(championData)

when i console.log championData, ChampionData is re-render twice. Fisrt time : It is undefined. Second time : It is a object. After i use it to .map() my code not run and console.log properties is undefined. I mean when i pass data through other components and console.log it . It also re-render twice like that. And i can't take properties of objects. The web will crash if i do it. I think it's because it is undefined in first time.

So how can i fix it, and re-render only once?

More infomation:

return (
    <div className="championView">
      <Navbar />
      <ChampionViewOverview index={location.state.index} championData={championData} />
      <Abilities index={location.state.index} championData={championData} />
      <Skin index={location.state.index} championData={championData} />
      <Footer />
    </div>
  )

when i take championData to ChampionViewOverView components and console.log(championData) It re-render twice and first undefined , second is Object.

It make my web crash. Thanks

I finally found a way to do it. that is i need to set the same properties as setChampionData(data). This helps when passing to other components and using the map function for the internal properties not to be missing and crash. Thanks for all supports.

CodePudding user response:

Re-renders occur when there is a change in state. The state is set only when the component is loaded, and further state updating in useEffect takes place after the component has mounted, since the state is updated in useEffect hook, the component is re-rendered. Since you mentioned, mapping, I can safely assume that the championData is an array. Although you can't really prevent re-rendering, you can definitely fix the crashing issue. In your parent component or in the child component, you could use the optional chaining operator ?, which maps or operates on a variable only when it has some value and is not undefined.

In your ChampionViewOverview component, you could use the optional chaining operator like below:

export default function ChampionViewOverview(props){
.
.
.
return(
<Fragment>
{props.championData?.map(obj=>return(//Return whatever)} //Optional chaining
</Fragment>
)

The above is just a sample and for demonstration purposes, shows how to use the optional chaining operator.

CodePudding user response:

I think that behaviour you are seeing is totally normal: you are trying to log a state value putting a console.log in component's body. Unfortunately you cannot do this in React because you don't know when component's body will be evaluated by React.

So first of all, I would move that log into an hook. So if you want to log the very last value of championData you could use useEffect in this way:

useEffect(() => {
   console.log(championData);
}, [championData])  //<-- this means, useEffect will be triggered every time championData changes

Second: you said that you are using championData somewhere in your code with a map BUT you initialized championData as an object:

const [championData, setChampionData] = useState({}) //<-- empty object

and map does not exists for JSON object. So I would change championData initialization in this way:

const [championData, setChampionData] = useState([]) //<-- void array instead of empty object
  • Related