Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - How to detect when all sub-components of a parent component are visible to the user?

Tags:

reactjs

TL;DR

How can a parent component know when the rendering of every child component under it has finished and the DOM visible to the user with its most up to date version?

Let's say I have a component A that has a child Grid component consisting of 3x3 grandchild components. Each of those grandchild components fetches data from a restful API endpoint and renders itself when the data becomes available.

I would like to cover the entire area of Component A with a loader placeholder, to be unveiled only when the last of the components in the grid has fetched the data successfully, and rendered it, such that it's already on the DOM and can be viewed.

The user experience should be a super smooth transition from "loader" to a fully populated grid without flickering.

My problem is knowing exactly when to unveil the components under the loader.

Is there any mechanism I can rely on to do this with absolute accuracy? I don't to hard code a time limit for the loader. As I understand relying on ComponentDidMount for every child is also unreliable as it doesn't actually guarantee the component is fully visible to the user at the time of the call.

To distill the question even further:

I have a component that renders some kind of data. After it's initialized it doesn't have it, so in its componentDidMount it hits an API endpoint for it. Once it receives the data, it's changing its state to reflect it. This understandably causes a re-render of the final state of that component. My question is this: How do I know when that re-render has taken place and is reflected in the User facing DOM. That point in time != the point in time when the component's state has changed to contain data.

like image 326
JasonGenX Avatar asked Feb 12 '20 15:02

JasonGenX


People also ask

How do you know if a component is Rerendered?

There's a checkbox well hidden in the React DevTools settings that allows you to visually highlight the components that rerendered. To enable it, go to "Profiler" >> click the "Cog wheel" on the right side of the top bar >> "General" tab >> Check the "Highlight updates when components render." checkbox.

Does React update all children components once the state of a parent has changed?

As a result, the child components only update when the parent component's state changes with one of those functions. Directly mutating the props object is not allowed since this won't trigger any changes, and React doesn't notice the changes.

Does child Rerender if parent Rerenders?

By default, React uses === to compare the previous props and the current props . As a result, the props are considered different between re-renders. That's why even though Child receives nothing from Parent as part of its props , it still gets re-rendered whenever Parent gets re-rendered – React.

How do you prevent re rendering of a React child component when its parent is updated?

OR, we can remove function memoization here, and just wrap ChildComponent in React. memo : MovingComponent will re-render, “children” function will be triggered, but its result will be memoized, so ChildComponent will never re-render.


1 Answers

There are two lifecycle hooks in React that are called after a component's DOM has rendered:

  • componentDidMount
  • componentDidUpdate (you're interested in this one)

For your use case your parent component P is interested when N child components have each satisfied some condition X. X can be defined as a sequence:

  • async operation completed
  • component has rendered

By combining the state of the component and using the componentDidUpdate hook, you can know when the sequence has completed and your component meets condition X.

You can keep track of when your async operation has completed by setting a state variable. For example:

this.setState({isFetched: true})

After setting state, React will call your components componentDidUpdate function. By comparing the current and previous state objects within this function you can signal to the parent component that your async operation has completed and your new component's state has rendered:

componentDidUpdate(_prevProps, prevState) {
  if (this.state.isFetched === true && this.state.isFetched !== prevState.isFetched) {
    this.props.componentHasMeaningfullyUpdated()
  }
}

In your P component, you can use a counter to keep track of how many children have meaningfully updated:

function onComponentHasMeaningfullyUpdated() {
  this.setState({counter: this.state.counter + 1})
}

Finally, by knowing the length of N you can know when all meaningful updates have occurred and act accordingly in your render method of P:

const childRenderingFinished = this.state.counter >= N
like image 193
Andrew Sinner Avatar answered Sep 19 '22 19:09

Andrew Sinner