Home > front end >  How to delay React from rendering child component until parent component finish fetching?
How to delay React from rendering child component until parent component finish fetching?

Time:05-27

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

  • Related