组件之间的 React 生命周期事件顺序有哪些保证?

What are the guarantees on React lifecycle event order between components?

跨不同组件的 React 生命周期顺序保证是什么?它们是否在任何地方都有明确的记录?

例如,假设我有:

<div>{ x ? <A /> : <B /> }</div>

此处,x 在 true 和 false 之间切换将卸载一个组件并安装另一个组件。

是否保证关于安装中涉及的生命周期事件在这些元素中触发的顺序?

例如,well documentedx 变为真时,A 的 render 将在 A 的 componentDidMount 之前触发。但是,是否保证 A 的 rendercomponentDidMount 总是在 B 的 componentWillUnmount 之后触发?

更进一步:如果 A 和 B 在这棵树中 children 更靠下,开关在顶部,它会改变吗?

欢迎任何答案,但非常感谢关于此的公司文档。

有时我很难理解 componentWillMount() 和 componentDidMount() 方法之间的根本区别。当一个人工作时,我一直感到沮丧,但我不明白如何或为什么。

componentWillMount() componentWillMount() 在 2018 年被弃用,直到一个在线学习小组 session 我才意识到的一个大障碍。虽然你可以继续使用它(直到 React 17.0),但它不是最佳实践,因为它对于异步渲染是不安全的。如果您选择继续使用它,您应该使用 UNSAFE_componentWillMount().

原因 在 componentWillMount() 中使用 fetch 调用会导致组件首先呈现空数据,因为 componentWillMount() 不会 return 在组件的第一次呈现之前。

由于 JavaScript 事件是异步的,当您进行 API 调用时,浏览器会在调用仍在进行时继续执行其他工作。使用 React,当组件正在渲染时,它不会等待 componentWillMount() 完成,所以组件会继续渲染。

综上所述,您需要创建一个组件,该组件在没有您希望显示的数据的情况下仍然看起来像样。在数据出现之前,没有办法(甚至没有计时器)停止组件渲染。

当我构建我的项目时,我不知道 componentWillMount() 的弃用,我不明白为什么我一直收到“无法读取未定义的 属性 'map'” 错误.我很困惑,因为我有一个数组,它应该被数据填充,但事实并非如此。

事实证明,这是完全合理的,因为组件最初是用空数据渲染的,所以数组是空的,无法迭代。只有在这一点上,我才知道使用 componentDidMount() 是最佳实践。

componentDidMount() 调用获取数据的最佳位置是在 componentDidMount() 中。 componentDidMount() 在客户端只被调用一次,而 componentWillMount() 被调用两次,一次在服务器端,一次在客户端。当客户端从服务器接收到数据并在浏览器中显示数据之前,在初始渲染之后调用它。由于在初始渲染后才会加载数据,因此开发人员需要正确设置组件的初始状态。

附带说明一下,借助 componentDidMount(),您可以使用计时器并每隔一段时间更新一次数据,而无需用户刷新页面。我想说这是一个非常巧妙的功能,可以在 projects/websites.

中使用。

componentDidUnmount 从未在条件渲染中调用。

我在 codepen 上测试并得到如下结果:

父组件

  constructor(props) {
    super(props);
    this.state = {
      flag: false,
    }
  }
  render() {
    const { flag } = this.state;
    return (
      <div>
        <h2 onClick={() => this.setState({flag: !flag})}>CONVERT</h2>
        <div>
          { flag ? <A /> : <B /> }
        </div>
      </div>
    );
  }

子组件 A


class A extends React.Component {
  componentDidMount() {
    console.log('A is mounted');
  }
  componentWillMount() {
    console.log('A will be mounted');
  }
  componentWillUnmount() {
    console.log('A will be unmounted');
  }
  componentDidUnmount() {
    console.log('A is unmounted');
  }
  render() {
    return (
      <h1>A</h1>
    );
  }
}

子组件 B

Almost the same as A :D

结果

我点击了两次CONVERT,结果是:

"B will be mounted"  // first render
"B is mounted"

"B will be unmounted" // first click
"A will be mounted"
"A is mounted"

"A will be unmounted" // second click
"B will be mounted"
"B is mounted"

componentDidUnmount was never called in conditional rendering.

如果你查看 React 官方仓库中的测试,你可以轻松找到与此订单相关的相关测试。


 it('prepares new child before unmounting old', () => {
    const log = [];

    class Spy extends React.Component {
      UNSAFE_componentWillMount() {
        log.push(this.props.name + ' componentWillMount');
      }
      render() {
        log.push(this.props.name + ' render');
        return <div />;
      }
      componentDidMount() {
        log.push(this.props.name + ' componentDidMount');
      }
      componentWillUnmount() {
        log.push(this.props.name + ' componentWillUnmount');
      }
    }

    class Wrapper extends React.Component {
      render() {
        return <Spy key={this.props.name} name={this.props.name} />;
      }
    }

    const container = document.createElement('div');
    ReactDOM.render(<Wrapper name="A" />, container);
    ReactDOM.render(<Wrapper name="B" />, container);

    expect(log).toEqual([
      'A componentWillMount',
      'A render',
      'A componentDidMount',

      'B componentWillMount',
      'B render',
      'A componentWillUnmount',
      'B componentDidMount',
    ]);
  });

The reasoning behind this implementation can be found @ commit message for test

"This matches what we do in Fiber -- and doing it this way is the only way we can prepare new views in the background before unmounting old ones."


同时查看 React 16.0.0 的最新更新日志:React 16.0.0 changelog

When replacing <A /> with <B />, B.componentWillMount now always
happens before A.componentWillUnmount. Previously,
A.componentWillUnmount could fire first in some cases.

所以这个顺序是有保证的!