How can I make sure a React parent component loads data before its child component mounts?

Viewed 3231

I'm writing a simple calendar application that uses a common layout to wrap different views of events (month view shows a larger calendar with all the days of the month and events for each day, week view just shows a vertical list of events for that week, etc.). The common layout includes a calendar picker control for selecting the date, and then a list of event categories that can be checked or unchecked to show events relating to sports, entertainment, etc.

When the layout mounts, I'm calling an async Redux action creator to get the list of event categories from the database. When those are retrieved, they're saved in a Redux store with a property of Selected set to true, since they're all selected at initial load.

async componentWillMount() {
    await this.props.getEventTypes();
}

When the month view, which is a child of the layout view, mounts, it's grabbing all the events for the given month. Part of the selection process of getting those events is sending the list of selected event categories to the backend so it only gets events from the selected categories.

async componentWillMount() {
    await this.props.getWeeks();
}

The problem is, the selected categories list is always empty when the month view goes to grab the events for the month. So it's not going to select anything since no categories are selected.

It seems the only way this can be happening is if the child component is mounting first, or if the parent component is taking so long to get the event categories that the getWeeks process finishes first (this is unlikely as the process to grab the weeks and days and their events is much more involved than just selecting the event category list).

So, how can I make sure the parent component grabs the event categories from the database and puts them in the Redux store before the child component selects its events?

I know one way, probably the best way, to do this would be to have the list of event categories render into the page on the server side, so it's just already present at initial load. I'll probably end up doing it that way, but I'd also like to know how to do it all through client-side actions, in case I need to do it that way for some reason in the future.

2 Answers

You can try like this

Set isDataLoaded when data is available.

Use ternary operator for conditional rendering.

In you render

return(
    <>
        ....
      { isDataLoaded ? <ChildComponent /> : null }

        ....other sutff
   </>
);

Use can also use the && operator

 return(
        <>
            ....
          { isDataLoaded && <ChildComponent /> }
    
            ....other sutff
       </>
    );

You can integrate componentDidUpdate() and use it to render your child-components in a somewhat synchronous flow.

Let's say the structure of your Parent Component should look something like the following:

Parent

class Parent extends React.Component{
   state = {
      renderChildren: false
   }

   async componentDidMount() {
      await this.props.getEventTypes();
   }

   componentDidUpdate(prevProps){
      if(this.props.yourUpdatedReducer !== prevProps.yourUpdatedReducer){
         this.setState({
            renderChildren: true
         })
      }
   }

   render(){ 
       const { renderChildren } = this.state 
       return(
         { renderChildren ? <Child/> : "Loading" }
       )
   }
}
  • You want a key in your state that determines whether you should render the Child component.
  • In componentDidMount(), you call the action-creator function, when it completes, you get updated props.
  • With updated props, you trigger componentDidUpdate(), where you check the values in your reducer. If the values are different that means you got the updated data from your database, so everything has loaded.
  • Great, so now you want to mount your Child component, so you update-state, setting renderChildren to true, thus re-rendering the Parent component. Now Child gets rendered and should behave as expected.
Related