In which order are parent-child components rendered?
Asked Answered
B

6

12

If I have Two Components (Parent & Child) like this :

1-The Parent (Countdown):

var Countdown = React.createClass({
  getInitialState: function(){
    return{count: 0};
  },
  handleSetCountdown: function(seconds){
    this.setState({
      count: seconds
    });
  },
  render:function(){
    var {count} = this.state;
    return(
      <div>
        <Clock totalSeconds={count}/>
        <CountdownForm onSetCountdown={this.handleSetCountdown} />
      </div>
    );
  }
});

module.exports =Countdown;

2-The Child (CountdownForm):

var CountdownForm = React.createClass({
  onSubmit: function(e){
    e.preventDefault();
    var strSeconds = this.refs.seconds.value;
    if(strSeconds.match(/^[0-9]*$/)){
      this.refs.seconds.value ='';
      this.props.onSetCountdown(parseInt(strSeconds,10));

    }
  },
  render: function(){
    return(
      <div>
        <form ref="form" onSubmit={this.onSubmit} className="countdown-form">
          <input type="text" ref="seconds" placeholder="Enter Time In Seconds"/>
          <button className="button expanded">Start</button>
        </form>
      </div>
    );
  }
});

module.exports = CountdownForm;

I'm confused about the life cycle (the order in which the components are rendered)?

Biramous answered 20/6, 2017 at 13:50 Comment(0)
S
36

I'm not immediately seeing a clear "this is the order of lifecycle events between parent and child" in the React docs, though I could be missing it.

It's trivial to determine empirically, of course:

class Child extends React.Component {
    constructor(...args) {
        super(...args);
        console.log("Child constructor");
    }
    componentWillMount(...args) {
        console.log("Child componentWillMount");
    }
    componentDidMount(...args) {
        console.log("Child componentDidMount");
    }
    render() {
        console.log("Child render");
        return <div>Hi there</div>;
    }
}

class Parent extends React.Component {
    constructor(...args) {
        super(...args);
        console.log("Parent constructor");
    }
    componentWillMount(...args) {
        console.log("Parent componentWillMount");
    }
    componentDidMount(...args) {
        console.log("Parent componentDidMount");
    }
    render() {
        console.log("Parent render start");
        const c = <Child />;
        console.log("Parent render end");
        return c;
    }
}

ReactDOM.render(<Parent />, document.getElementById("react"));
.as-console-wrapper {
  max-height: 100% !important;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

That shows us the order:

Parent constructor
Parent componentWillMount
Parent render start
Parent render end
Child constructor
Child componentWillMount
Child render
Child componentDidMount
Parent componentDidMount

Which got me wondering about the order of children within a parent, so:

class Child extends React.Component {
    constructor(props, ...rest) {
        super(props, ...rest);
        console.log(this.props.name + " constructor");
    }
    componentWillMount(...args) {
        console.log(this.props.name + " componentWillMount");
    }
    componentDidMount(...args) {
        console.log(this.props.name + " componentDidMount");
    }
    render() {
        console.log(this.props.name + " render");
        return <div>Hi from {this.props.name}!</div>;
    }
}

class Parent extends React.Component {
    constructor(...args) {
        super(...args);
        console.log("Parent constructor");
    }
    componentWillMount(...args) {
        console.log("Parent componentWillMount");
    }
    componentDidMount(...args) {
        console.log("Parent componentDidMount");
    }
    render() {
        console.log("Parent render start");
        const result =
            <div>
                <Child name="Child1" />
                <Child name="Child2" />
            </div>;
        console.log("Parent render end");
        return result;
    }
}

ReactDOM.render(<Parent />, document.getElementById("react"));
.as-console-wrapper {
  max-height: 100% !important;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Which gives us:

Parent constructor
Parent componentWillMount
Parent render start
Parent render end
Child1 constructor
Child1 componentWillMount
Child1 render
Child2 constructor
Child2 componentWillMount
Child2 render
Child1 componentDidMount
Child2 componentDidMount
Parent componentDidMount

Not at all surprising, but good to double-check. :-)

Subclinical answered 20/6, 2017 at 14:1 Comment(0)
F
6

Just adding componentWillUnmount to the cycle:

class Child extends React.Component {
    constructor(props, ...rest) {
        super(props, ...rest);
        console.log(this.props.name + " constructor");
    }
    componentWillMount(...args) {
        console.log(this.props.name + " componentWillMount");
    }
    componentWillUnmount(...args) {
        console.log(this.props.name + " componentWillUnmount");
    }
    componentDidMount(...args) {
        console.log(this.props.name + " componentDidMount");
    }
    render() {
        console.log(this.props.name + " render");
        return <div>Hi from {this.props.name}!</div>;
    }
}

class Parent extends React.Component {
    constructor(...args) {
        super(...args);
        console.log("Parent constructor");
    }
    componentWillMount(...args) {
        console.log("Parent componentWillMount");
    }
    componentWillUnmount(...args) {
        console.log("Parent componentWillUnmount");
    }
    componentDidMount(...args) {
        console.log("Parent componentDidMount");
    }
    render() {
        console.log("Parent render start");
        const result =
            <div>
                <Child name="Child1" />
                <Child name="Child2" />
            </div>;
        console.log("Parent render end");
        return result;
    }
}

class ParentWrapper extends React.Component {
    constructor(...args) {
        super(...args);
        this.state = { showParent: true };
        setTimeout(() => { this.setState({ showParent: false }) });
    }
    render() {
        return <div>{this.state.showParent ? <Parent /> : ''}</div>;
    }
}

ReactDOM.render(<ParentWrapper />, document.getElementById("react"));
.as-console-wrapper {
  max-height: 100% !important;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

result :

Parent constructor
Parent componentWillMount
Parent render start
Parent render end
Child1 constructor
Child1 componentWillMount
Child1 render
Child2 constructor
Child2 componentWillMount
Child2 render
Child1 componentDidMount
Child2 componentDidMount
Parent componentDidMount
Parent componentWillUnmount
Child1 componentWillUnmount
Child2 componentWillUnmount
Faradism answered 3/5, 2020 at 11:45 Comment(0)
T
2

a live demo

react-parent-child-lifecycle-order

https://33qrr.csb.app/

https://codesandbox.io/s/react-parent-child-lifecycle-order-33qrr

create order


parent constructor
parent WillMount
parent render

child constructor
child WillMount
child render
child DidMount

parent DidMount

destroy order

parent WillUnmount

child WillUnmount

// child unmount

// parent unmount



Trella answered 18/5, 2020 at 3:8 Comment(0)
P
2

With functional components like this one...

  const MyComponent = () => {
    useEffect(() => {
      trackingFn("useEffect MyComponent");
      subscriptions.push(() => { trackingFn("subscription MyComponent") })
      return () => trackingFn("useEffect cleanup MyComponent");
    })
    useLayoutEffect(() => {
      trackingFn("useLayoutEffect MyComponent");
      return () => trackingFn("useLayoutEffect cleanup MyComponent");
    })
    try {
      trackingFn("render MyComponent");
      return <MyChildComponent />
    } finally {
      trackingFn("finally MyComponent");
    }
  }

The order is

  1. render
  2. finally block
  3. useLayoutEffect
  4. useEffect
  5. useLayoutEffect cleanup
  6. useEffect cleanup

This is shown by this test.

import { act, render } from "@testing-library/react";
import { useEffect, useLayoutEffect } from "react";

it("should call the methods in the expected order", async () => {

  const trackingFn = jest.fn();
  const subscriptions: (() => void)[] = [];

  const MyGrandChildComponent = () => {
    useEffect(() => {
      trackingFn("useEffect MyGrandChildComponent");
      subscriptions.push(() => { trackingFn("subscription MyGrandChildComponent") })
      return () => trackingFn("useEffect cleanup MyGrandChildComponent");
    })
    useLayoutEffect(() => {
      trackingFn("useLayoutEffect MyGrandChildComponent");
      return () => trackingFn("useLayoutEffect cleanup MyGrandChildComponent");
    })
    try {
      trackingFn("render MyGrandChildComponent");
      return <div />
    } finally {
      trackingFn("finally MyGrandChildComponent");
    }
  }

  const MyChildComponent = () => {
    useEffect(() => {
      trackingFn("useEffect MyChildComponent");
      subscriptions.push(() => { trackingFn("subscription MyChildComponent") })
      return () => trackingFn("useEffect cleanup MyChildComponent");
    })
    useLayoutEffect(() => {
      trackingFn("useLayoutEffect MyChildComponent");
      return () => trackingFn("useLayoutEffect cleanup MyChildComponent");
    })
    try {
      trackingFn("render MyChildComponent");
      return <MyGrandChildComponent />
    } finally {
      trackingFn("finally MyChildComponent");
    }
  }

  const MyComponent = () => {
    useEffect(() => {
      trackingFn("useEffect MyComponent");
      subscriptions.push(() => { trackingFn("subscription MyComponent") })
      return () => trackingFn("useEffect cleanup MyComponent");
    })
    useLayoutEffect(() => {
      trackingFn("useLayoutEffect MyComponent");
      return () => trackingFn("useLayoutEffect cleanup MyComponent");
    })
    try {
      trackingFn("render MyComponent");
      return <MyChildComponent />
    } finally {
      trackingFn("finally MyComponent");
    }
  }

  const { unmount } = render(<MyComponent />);

  await act(() => Promise.resolve());
  subscriptions.forEach(f => f());
  unmount();
  expect(trackingFn.mock.calls).toEqual([
    ['render MyComponent'],
    ['finally MyComponent'],
    ['render MyChildComponent'],
    ['finally MyChildComponent'],
    ['render MyGrandChildComponent'],
    ['finally MyGrandChildComponent'],
    ['useLayoutEffect MyGrandChildComponent'],
    ['useLayoutEffect MyChildComponent'],
    ['useLayoutEffect MyComponent'],
    ['useEffect MyGrandChildComponent'],
    ['useEffect MyChildComponent'],
    ['useEffect MyComponent'],
    ['subscription MyGrandChildComponent'],
    ['subscription MyChildComponent'],
    ['subscription MyComponent'],
    ['useLayoutEffect cleanup MyComponent'],
    ['useLayoutEffect cleanup MyChildComponent'],
    ['useLayoutEffect cleanup MyGrandChildComponent'],
    ['useEffect cleanup MyComponent'],
    ['useEffect cleanup MyChildComponent'],
    ['useEffect cleanup MyGrandChildComponent']
  ])
});
``
Pantry answered 3/2, 2023 at 2:1 Comment(0)
C
1

The render order is executed in the order of the react component tree, however, the mount order is in the reverse order (with the inner-most child component mounting first.)

Chuch answered 21/2, 2021 at 23:34 Comment(0)
F
0

It is possible to make the parent's ComponentDidMount method execute before the child's ComponentDidMount method. And here is how it is done.

Inside the first div in the parent, before anything is rendered, check if state is set yet. Only render anything if state is set.

Example:

render() {
  const { t } = this.props;
  return (
    <div>
      {this.state.row !== null ? (
            ...
      ) : null}
    </div>
  );
}
Faeroese answered 10/11, 2021 at 14:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.