I have a parent component that fetch from webAPI and will return an array of people. I also have a child component that suppose to receive props from parent, in which props is obtained from the fetch
The problem I am having is it seems react renders the child component before the fetch is completed. Here is my code:
import React from "react";
import { Person} from "./Components";
export function Body({departmentId}){
const [peopleArray,setPeopleArray] = React.useState([]);
React.useEffect(
() => {
const getPeopleArr = async () => {
const response = await
fetch(`https://xxxx`);
const data = await response.json();
setPeopleArray(data);
};
getPeopleArr();
}, []);
return(
<div>
<Person name={peopleArray[0].personalDetails.name} position={peopleArray[0].department.name}/>
</div>
);
}
as you can see from above, initially peopleArray is an empty array. and react will give me the error "Uncaught TypeError: Cannot read properties of undefined (reading 'personalDetails')"
if I remove the code<Person name={peopleArray[0].personalDetails.name} position={peopleArray[0].department.name}/>
wait a bit until it fetched, and then quickly paste the code, it displays the Person component successfully.
I attempted to add SetTimeOut in the child component and try to delay it for few seconds, by doing:
setTimeout(() => {
}, "2000")
but this doesn't seem to help the issue.
Could you please suggest smthg that I can do to delay the child component from rendering before the fetch completed? Thank you
Edit: one thing I can think of was to create multiple useState(s) for each props I want to pass to the child, for example
const [name,SetName] = data[0].personalDetails.name;
...
<Person name = {name}>
but then if I have 20 props to pass, then I have to create 20 useState(s)
EDIT: The ? works... thanks guys :D
CodePudding user response:
just introduce a state like "loaded" and then render your child component conditionally:
loaded && <ChildComponent />
In your case:
import React from "react";
import { Person} from "./Components";
export function Body({departmentId}){
const [peopleArray,setPeopleArray] = React.useState([]);
const [loaded, setLoaded] = React.useState(false);
React.useEffect(
() => {
const getPeopleArr = async () => {
const response = await
fetch(`https://xxxx`);
const data = await response.json();
setPeopleArray(data);
};
await getPeopleArr();
setLoaded(true);
}, []);
return(
<div>
{loaded && <Person name={peopleArray[0].personalDetails.name} position={peopleArray[0].department.name}/>}
</div>
);
}
CodePudding user response:
you should check first if the variable is not empty:
{peopleArray.length && <Person name={peopleArray[0]?.personalDetails.name} position={peopleArray[0]?.department.name}/> }
CodePudding user response:
There are several ways to solve this problem.
The simplest is to use optional chaining. Something like this:
<Person name={peopleArray[0]?.personalDetails?.name} position={peopleArray[0]?.department?.name}/>
If peopleArray[0]
is undefined, it won't render anything
Another solution is to use the length:
peopleArray.length > 0 && <Person name={peopleArray[0].personalDetails.name} position={peopleArray[0].department.name}/>
The third one is to introduce a loading state, which can be true by default, and you can change it to false after the fetch
promise has been resolved
CodePudding user response:
You can add "?" after peopleArray[0]
to avoid error:
<Person name={peopleArray[0]?.personalDetails.name} position={peopleArray[0]?.department.name}/>
Or you can use conditional render:
<div>
{peopleArray[0] ? <Person name={peopleArray[0].personalDetails.name}
position={peopleArray[0].department.name}/> : null }
</div>
CodePudding user response:
Quick fix, is just to check the array's length:
return peopleArray.length ? <Person name={peopleArray[0].personalDetails.name} position={peopleArray[0].department.name}/> : <></>
Though it doesn't consider the case when fetched result is empty array