【问题标题】:ReactJS: How many times is a mapped component's constructor called?ReactJS:映射组件的构造函数调用了多少次?
【发布时间】:2019-04-02 20:53:36
【问题描述】:

我有一个 React 组件,它在另一个组件中使用 Array.prototype.map() 多次呈现。

第一个组件的构造函数应该被调用多少次?我期望映射数组的长度是映射数组的长度,但它似乎只被调用一次。

在我的具体代码中,它是在最后一个 render() 之前调用的。

示例代码:

class ComponentB extends Component {
  constructor(props) {
    super(props) // added upon first reply
    this.handleObjectAdd = this.handleObject.bind(this);
    this.state.objects = [];
  }

  handleObjectAdd() {
    this.state.objects.unshift({prop1: this.state.objects.length + 1});
  }

  render() {
      return (
        <div>
          <button onClick={this.handleObjectAdd}>ADD</button>
          { this.state.objects.map((object, index) =>
              <ComponentA key={index} details={object}/>
            )
          }
        </div>
      )
    })
  }
}

class ComponentA extends Component {
  constructor(props) {
    super(props) // added upon first reply
    console.log('ComponentA constructor called');
  }
  render() {
    console.log('ComponentA render() called');
    return (
      <input type="text" value={this.props.details.prop1}></input>
    )
  }
}

对于一个包含 5 个元素的数组(所有 ComponentA 实例),我得到以下几行:

ComponentA render() called
ComponentA render() called
ComponentA render() called
ComponentA render() called
ComponentA constructor called
ComponentA render() called

此外,构造函数的日志行总是出现在最后一个构造函数日志行之前,与数组元素的数量无关。

为什么会出现上面这样的日志输出?希望有任何见解。

这被标记为与React Rerender dynamic Components (Solr) 重复,但事实并非如此。

【问题讨论】:

    标签: reactjs constructor components render array.prototype.map


    【解决方案1】:

    我将您的问题理解为“为什么当我将新元素放在数组的最开始时,似乎为最后一个元素创建了新的 React 组件?”

    原因是您将keyindex 一起使用。

    基于其逻辑(检查文档中的Lists and Keys 部分),因为只有最后一个元素(新索引为oldLength + 1)具有唯一的key,只有它会从头开始创建。而所有其他只是重新渲染和更新。换句话说,您的代码会更新 N - 1 元素,而不是只创建 1 个新元素并保持所有其他元素不变。

    要处理这个问题,您不应依赖 key 中的 index,而应使用其他一些可预测、稳定且独特的值。在您的情况下,它是prop1。然后将为第一个元素调用构造函数。

    这里是更新版本

    class ComponentB extends Component {
      constructor(props) {
        super(props) // added upon first reply
        this.state = {
          objects: []
        };
      }
    
      handleObjectAdd = () => {
        this.setState(oldState => ({
          objects: [
              {prop1: this.state.objects.length + 1}, 
              ...oldState.objects
          ]
        }))
      }
    
    
      render() {
        return (
          <div>
            <button onClick={this.handleObjectAdd}>ADD</button>
            {this.state.objects.map(obj =>
              <ComponentA key={obj.prop1} details={obj} />
            )}
          </div>
        )
      }
    }
    
    class ComponentA extends Component {
      constructor(props) {
        super(props) // added upon first reply
        console.log('ComponentA constructor called');
      }
      render() {
        console.log('ComponentA render() called');
        return (
          <input type="text" value={this.props.details.prop1}></input>
        )
      }
    }
    

    您的初始代码几乎没有语法错误并且直接改变了状态(永远不要这样做),因此在单击按钮后它永远不会重新渲染。请在下次发布问题时检查代码 - 这会使理解/回答变得更加容易。

    [UPD] 从下面的 cmets 中提取了一些切片

    而不是仅仅创建 1 个新的而让所有其他的保持不变。

    React 不会尝试最小化操作数。在比较 [1,2,3] 和 [0, 1, 2, 3] 时,有两种可能的方法:“在开头插入 0,移动其他所有内容”或“将所有元素减 1,然后在结束'。如果你提供好的属性作为关键,React 会选择第一个解决方案。但是拥有 key={index} 你实际上是在说'React,使用第二种方法,我知道我在做什么'。 React 不会分析你在调用 .setState 之前运行的代码,它只是依赖于键值。

    【讨论】:

    • 感谢您的回答。我在我的实际代码中发布了一个我已经修复的旧代码(变异状态的一部分)的错误。我也很感谢您对键的解释,这加强了我对列表和键的理解。你回答了为什么只有一次调用构造函数(我很难理解为什么它没有被重复调用)。我的另一个问题是:当我在列表的开头添加新元素时,为什么它会在最后一个元素的第二个被调用?我会在我的初始代码中留下错误。
    • 看,React 并没有尝试最小化操作数。在比较[1,2,3][0, 1, 2, 3] 时,有两种可能的方法:“在开头插入 0,移动其他所有内容”或“将所有元素减 1,然后在末尾插入 3”。如果您提供良好的属性作为key React 选择第一个解决方案。但是拥有key={index},你实际上是在说'React,使用第二种方法,我知道我在做什么'。 React 不会分析你在调用 .setState 之前运行的代码,它只依赖于 key 值。
    • 我想我现在明白为什么在最后一次渲染之前调用它:最后一次调用 render 实际上是针对添加的最后一个元素(在这种情况下位于列表的开头),而不是针对他最后一个元素(存在于列表中)。所以,我认为 React 调用现有元素的渲染,然后调用新元素的构造函数,然后调用这个新元素的渲染。
    • 是的,没错。最后一个被创建,下一个被渲染。
    • 您对反应最小化操作的最后评论对于理解有关键和索引的文档非常有帮助。谢谢
    猜你喜欢
    • 2019-12-02
    • 1970-01-01
    • 2018-08-18
    • 2020-06-17
    • 1970-01-01
    • 2012-09-24
    • 1970-01-01
    • 2019-08-02
    • 1970-01-01
    相关资源
    最近更新 更多