4

I need to execute a piece of code after all grandchildren of a Component are rendered to scroll to one of the grandchildren. The structure looks like this:

`<GrandParent>
   <IntermediateParent>
     <IntermediateParent2>
       <GrandChild/>
       <GrandChild/>
       <GrandChild/>
       <GrandChild/>
     </IntermediateParent2>
   </IntermediateParent>
 </GrandParent>`

The render method of GrandParent looks like this:

render() {
  if (!this.props.listOfGrandchildren) { // still loading data from server
    return <div>LOADING...</div>
  }

  return <IntermediateParent grandchildren={this.props.listOfGrandchildren} />
}

It is clear that using ComponentDidMount will clearly not work because of the children being mounted at a later time, after the data is loaded from the server. In this case, CDM of GrandParent would be triggered before CDM of any GrandChild

I could pass down a method from top to each GrandChild that would be called at CDM. On GrandParent I would wait for all GrandChild to call that method by using a counter and once the counter would be equal to the number of grandchildren I could call my piece of code that would scroll to the wanted grandchild, but this feels like a lot of hassle.

I want to load the GrandParent before the data comes down from the server, to render a placeholder/loading element. I am looking for a more elegant method to solve this.

UPDATE: Is componentDidMount of parent called after all componentDidMount of children?

I know why my GrandChildren's CDM is triggered after CDM of GrandParent just looking to have a different behaviour

Community
  • 1
  • 1
Tiberiu Maxim
  • 1,474
  • 14
  • 24

2 Answers2

3

In the end the most neat solution was the following:

I set up ComponentDidMount on IntermediateParent (works with IntermediateParent2 as well) which calls an action creator that sets a flag loadedX = true.

Then, in GrandParent's ComponentWillReceiveProps wait for the loadedX prop to become true. When this happens, manually call the desired 'callback' function. I inject the loadedX prop to GrandParent using connect.

Hope this also helps someone else. If you need more details ask in comments. I can also come up with some code for a more real world example.

Tiberiu Maxim
  • 1,474
  • 14
  • 24
1

We know that the children will render() when the parent render()s. This means we need to hook onto a parent render() and see if the children exist yet. We don't want to hook onto a child render() because that will run too many times and is kind of out of the scope of the child.

Looking at the React Component Lifecycle, we see that there are two ways to hook on after a parent render(), componentDidMount() and componentDidUpdate(). We know that we can't use componentDidMount() because the parent mounts before the children. However, we can use componentDidUpdate(). In your componentDidUpdate(), you can use refs, document.querySelector(), document.getElementById(), etc. to get a reference to the child. Once you are able to get the child reference, set a boolean flag so you only run the callback inside componentDidUpdate() once.

I feel like this is a cleaner solution. I think the accepted answer is kind of roundabout. You modify the IntermediateParent with ComponentDidMount(), actions using a dispatched redux action or a callback function in parent, and then the componentWillReceiveProps(). The componentWillReceiveProps() will also require a boolean flag like my solution.


Actually, maybe it isn't out of the scope of the child. Another solution is for the parent to pass a callback down to the child to call during the child's componentDidMount(). This callback will be called each time a child mounts, but you can keep a boolean flag in the parent so that the code only runs once.

dosentmatter
  • 1,494
  • 1
  • 16
  • 23