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.
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.
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.
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.
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.
There are two lifecycle hooks in React that are called after a component's DOM has rendered:
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:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With