最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React componentDidMount timing - Stack Overflow

programmeradmin1浏览0评论

Is ponentDidMount lifecycle method independent from sibling ponents? From example below it seems like it is invoked only after all sibling ponents have been mounted.

Let's say we have a top level ponent which renders 2 child ponents, first having simple render() and another having relatively slower render().

Sample to reproduce:

TL;DR:


class SlowComponent extends Component {
  ponentDidMount() {
    // perf mark
  }

  render() {
    // Simulate slow render
    // Takes 50ms
    return <h3>Slow ponent</h3>;
  }
}

class FastComponent extends Component {
  ponentDidMount() {
    // perf mark
  }

  render() {
    return <h3>Fast ponent</h3>;
  }
}

class App extends Component {
  constructor(props) {
    super(props);

    // perf mark start
  }

  ponentDidMount() {
    // perf mark
    // measure all marks and print
  }

  render() {
    return (
      <div>
        <FastComponent />
        <SlowComponent />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

I expect ponentDidMount timings to be like this:

  1. FastComponent 10 ms
  2. SlowComponent 50 ms
  3. App 52 ms

But in reality what I get is both fast and slow ponent ponentDidMount callbacks are fired at the same time, i.e.

  1. FastComponent 50 ms
  2. SlowComponent 51 ms
  3. App 52 ms

Current sample and repro code uses mount callback but same applies to ponentDidUpdate.

Full source:

import ReactDOM from "react-dom";
import React, { Component } from "react";

class SlowComponent extends Component {
  ponentDidMount() {
    performance.mark("slow-mounted");
  }

  render() {
    // Simulate slow render
    for (var i = 0; i < 10000; i++) {
      for (var j = 0; j < 100; j++) {
        const b = JSON.parse(
          JSON.stringify({
            test: "test" + i,
            test1: i * i * i
          })
        );
      }
    }
    return <h3>Slow ponent</h3>;
  }
}

class FastComponent extends Component {
  ponentDidMount() {
    performance.mark("fast-mounted");
  }

  render() {
    return <h3>Fast ponent</h3>;
  }
}

class App extends Component {
  constructor(props) {
    super(props);

    performance.mark("init");
  }

  ponentDidMount() {
    performance.mark("app-mounted");

    performance.measure("slow", "init", "slow-mounted");
    performance.measure("fast", "init", "fast-mounted");
    performance.measure("app", "init", "app-mounted");

    console.clear();

    console.log(
      "slow",
      Math.round(performance.getEntriesByName("slow")[0].duration)
    );
    console.log(
      "fast",
      Math.round(performance.getEntriesByName("fast")[0].duration)
    );
    console.log(
      "app",
      Math.round(performance.getEntriesByName("app")[0].duration)
    );

    performance.clearMarks();
    performance.clearMeasures();
  }

  render() {
    return (
      <div>
        <h1>Demo</h1>
        <FastComponent />
        <SlowComponent />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

Is ponentDidMount lifecycle method independent from sibling ponents? From example below it seems like it is invoked only after all sibling ponents have been mounted.

Let's say we have a top level ponent which renders 2 child ponents, first having simple render() and another having relatively slower render().

Sample to reproduce: https://codesandbox.io/s/j43klml9py?expanddevtools=1

TL;DR:


class SlowComponent extends Component {
  ponentDidMount() {
    // perf mark
  }

  render() {
    // Simulate slow render
    // Takes 50ms
    return <h3>Slow ponent</h3>;
  }
}

class FastComponent extends Component {
  ponentDidMount() {
    // perf mark
  }

  render() {
    return <h3>Fast ponent</h3>;
  }
}

class App extends Component {
  constructor(props) {
    super(props);

    // perf mark start
  }

  ponentDidMount() {
    // perf mark
    // measure all marks and print
  }

  render() {
    return (
      <div>
        <FastComponent />
        <SlowComponent />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

I expect ponentDidMount timings to be like this:

  1. FastComponent 10 ms
  2. SlowComponent 50 ms
  3. App 52 ms

But in reality what I get is both fast and slow ponent ponentDidMount callbacks are fired at the same time, i.e.

  1. FastComponent 50 ms
  2. SlowComponent 51 ms
  3. App 52 ms

Current sample and repro code uses mount callback but same applies to ponentDidUpdate.

Full source:

import ReactDOM from "react-dom";
import React, { Component } from "react";

class SlowComponent extends Component {
  ponentDidMount() {
    performance.mark("slow-mounted");
  }

  render() {
    // Simulate slow render
    for (var i = 0; i < 10000; i++) {
      for (var j = 0; j < 100; j++) {
        const b = JSON.parse(
          JSON.stringify({
            test: "test" + i,
            test1: i * i * i
          })
        );
      }
    }
    return <h3>Slow ponent</h3>;
  }
}

class FastComponent extends Component {
  ponentDidMount() {
    performance.mark("fast-mounted");
  }

  render() {
    return <h3>Fast ponent</h3>;
  }
}

class App extends Component {
  constructor(props) {
    super(props);

    performance.mark("init");
  }

  ponentDidMount() {
    performance.mark("app-mounted");

    performance.measure("slow", "init", "slow-mounted");
    performance.measure("fast", "init", "fast-mounted");
    performance.measure("app", "init", "app-mounted");

    console.clear();

    console.log(
      "slow",
      Math.round(performance.getEntriesByName("slow")[0].duration)
    );
    console.log(
      "fast",
      Math.round(performance.getEntriesByName("fast")[0].duration)
    );
    console.log(
      "app",
      Math.round(performance.getEntriesByName("app")[0].duration)
    );

    performance.clearMarks();
    performance.clearMeasures();
  }

  render() {
    return (
      <div>
        <h1>Demo</h1>
        <FastComponent />
        <SlowComponent />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
Share Improve this question asked Sep 5, 2018 at 12:40 LevLev 3,9246 gold badges44 silver badges59 bronze badges 2
  • Quick answer: they are not independent. See stackoverflow./questions/32814970/… – keul Commented Sep 5, 2018 at 12:43
  • That one is talking about parent/child ponent dependency, my current question is regarding children ponents. – Lev Commented Sep 5, 2018 at 14:06
Add a ment  | 

4 Answers 4

Reset to default 5 +500

I assume you are using the latest React version (16.5.0).

Assuming two ponents, one slow and one fast wrapped in a parent ponent the execution flow will be the following:

  1. Parent ponent calls its UNSAFE_ponentWillMount method
  2. Iterates all children with the order they are defined in its render method and calls their UNSAFE_ponentWillMount method
  3. Iterate all children and call their render method(here your slow ponent expensive render method will kick-in)
  4. Iterate all children and call their ponentDidMount method
  5. Parent ponent calls its ponentDidMount method

This whole process is being done synchronously.

Going back to your example, this is why you see almost the same timing to both your ponents, because all ponent lifecycle methods are called as soon as the work is pleted for all ponents.

In general, conceptually React consists of 2 phases, "render" phase and "mit" phase. The "mit" phase is when React applies any changes, and in your example the lifecycle method ponentDidMount is called in this phase.

You can follow this flow by debugging React, and viewing the stack trace which is the following:

ponentDidMount
mitLifeCycles
mitAllLifeCycles
callCallback
invokeGuardedCallbackDev
invokeGuardedCallback
mitRoot

As per your life cycle order, your ponentDidMount will be called (as the name itself suggests) after your ponent has been mounted. Which will be after the DOM has been mounted, which in doing so will call your render.

Now assuming if there is no I/O i.e, async event happening in your child ponent anywhere.

(and even if there are any async event happening, the functions will be executed but the call stack will maintain the execution in the event loop.. but this is a separate discussion)

-- Parent
  -- Child-1
  -- Child-2

Order of execution

Parent constructor()
Child 1 constructor()
Child 1 render()
Child 1 ponentDidMount()
Child 2 constructor()
Child 2 render()
Child 2 ponentDidMount()
Parent ponentDidMount()

As per the ReactJS docs the mounting triggers in order are

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • ponentDidMount()

Reference of mounting order from react documentation Mounting React Docs

On a side note, another detailed read on the order of execution. Understanding React — Component life-cycle

The simple answer

All ponents will fire ponentDidMount method after all renders are pleted. Because before to mount elements to DOM we need to create them.

Yes, ComponentDidMount lifecycle method is independent from sibling ponents. It depends on how your render takes time because it invokes after your render plete. But the ponent mounts in same order as they are in parent ponent. E.g - In above example first fast ponent mounts then second ponent.

And you can't calculate how much time a function takes to iterate a loops. So here your assumption of calculating time of slow ponent's ComponentDidMount lifecycle method was wrong. You can't expect specific time estimate based on number of iteration without profiling.

发布评论

评论列表(0)

  1. 暂无评论