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