【问题标题】:When does React create a new component instance?React 什么时候创建一个新的组件实例?
【发布时间】:2019-01-30 01:11:38
【问题描述】:

我对 React 中的构造函数有疑问

https://codesandbox.io/s/lpr147kmyl的例子

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class SimpleComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      number: props.number,
    }
    console.log('constructor', this.state.number)
  }
  render() {
    return (
      <div>
        {this.props.number}
      </div>
    )
  }
}

class App extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      showFirstComponent: true,
    }

    setInterval(() => (
      this.setState(previousState => (
        { showFirstComponent: !previousState.showFirstComponent }
      ))
    ), 1000);
  }
  render(){
    let c1 = <SimpleComponent number="1" />
    let c2 = <SimpleComponent number="2" />
    return (
      <div className="App">
        <h1>
          {this.state.showFirstComponent ? c1 : c2}
        </h1>
      </div>
    );
  }
}

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

我检查了控制台日志,只看到一条日志消息。 当我创建两个 SimpleComponent 实例时,我认为那里应该有两条日志消息:

let c1 = <SimpleComponent number="1" />
let c2 = <SimpleComponent number="2" />

我的问题是:

React 什么时候创建新的组件实例并调用组件的构造函数?

谢谢,

【问题讨论】:

  • 看你的例子,它看起来像是在渲染组件时,例如三元运算符的结果(只有 1 个分量)。

标签: reactjs constructor


【解决方案1】:

根据the docconstructor 仅在组件安装之前调用。如果您在代码示例中再添加两个 React Lifecycle 函数:componentDidMountcomponentWillUnmount,您将看到组件在整个过程中只挂载一次,而与状态更改的时间无关。如果您随后将shouldComponentUpdate 添加到组件中,您将看到只有一个SimpleComponent 实例,并且上层状态更改显示为SimpleComponent 上的props 更改。

您在这里看不到安装和卸载两个实例的原因是变量c1c2 不是SimpleComponent 的实例,因为通常和方便地假设。相反,它们是ReactElement,它是组件实例的描述

React 中的主要类型是 ReactElement。它有四个属性:type、props、key 和 ref。它没有方法,原型上也没有。

React (Virtual) DOM Terminology

只有在渲染元素时,才会创建组件的实例,并在各自的时间点调用 constructor 和生命周期函数。

那么,为什么像您的示例一样,元素 c2 永远不会用于创建组件?这可以追溯到 React 如何决定何时以及在特定时间更新什么(您可以阅读更多关于它的信息here

当组件更新时,实例保持不变,因此状态在渲染之间保持不变。

在您的示例中,当 App 组件呈现 c2 而不是 c1 时,React 会看到新组件元素与旧元素相同,因此它重用组件(即仅存在一个实例)并相应地更新道具/状态。

以下是我认为有助于理解此问题的更多阅读材料: https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/birth_mounting_indepth.html

Mark Amery 对this question 的回答也很有帮助。

【讨论】:

  • 我更新了示例,添加了 componentDidMount 和 componentWillUnmount。不会调用 componentWillUnmount。我认为 React 会卸载 C1,然后安装 C2 实例。但事实并非如此。请看示例codesandbox.io/s/lpr147kmyl
  • @Trung 我更新了帖子,希望能解释为什么没有你想象的两个实例(c1c2)。如果有帮助,请告诉我。
【解决方案2】:

如果有人为 react-native 登陆这里。 为不同的实例分配不同的keys 将创建新的实例,而不是重复使用同一个实例。

   <MyComponent
       key={this.state.uniqueInstanceKey}
       myData={this.state.myData}/>

【讨论】:

    【解决方案3】:

    您的三元组正在检查showFirstComponent 的状态并只呈现一个。

    {this.state.showFirstComponent ? c1 : c2}
    

    如果你把它全部删除,然后就这样做

    <SimpleComponent number="1" />
    <SimpleComponent number="2" />
    

    您将获得两个日志。为了进一步回答这个问题,constructor 在组件安装时被调用,所以通常一次,除非你动态安装东西。回到你的三元组:

    {this.state.showFirstComponent ? c1 : c2}
    

    如果您要使用按钮切换this.state.showFirstComponent,可以说:

    <button onClick={() => this.setState({ 
        showFirstComponent: !this.state.showFirstComponent
    })> 
      Toggle c1/c2 
    </button>
    

    每次您点击此按钮时,都会卸载组件并安装另一个组件,因此您将获得控制台日志。这是安装和卸载之间react lifecycle methods 的顺序。但是,如果你做任何其他事情,例如更改传递的道具,更改&lt;SimpleComponent /&gt; 的内部组件状态,它将卸载,而是调用渲染方法的更新,而不是构造函数。我建议在 &lt;SimpleComponent /&gt; 中为某些生命周期方法添加 console.log 并尝试触发它们,以更好地了解组件生命周期。或read an article about it

    【讨论】:

    • 我更新了codesandbox.io/s/lpr147kmyl 只有一条日志消息。我不确定这个例子有什么问题
    • 没什么,您将它们分配给变量 ONCE,并在需要时调用它们。如果您要执行以下操作:codesandbox.io/s/n9nmp7n7jj,它将永远调用构造函数。按照您的方式,您将它们分配给变量一次,然后基本上根据需要显示它们,但是当您执行 let c1 = ... 时,构造,甚至是您的情况下的渲染,仅被调用一次。
    • 您还可以使用 key 强制执行两个单独的组件。
    猜你喜欢
    • 2018-03-02
    • 2019-06-13
    • 1970-01-01
    • 2011-01-11
    • 1970-01-01
    • 2014-09-14
    • 2023-02-20
    • 2015-03-22
    • 2012-11-13
    相关资源
    最近更新 更多