ReactJS:映射的组件的构造函数被调用多少次?

时间:2018-10-29 21:00:04

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

我有一个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)的副本,但不是。

1 个答案:

答案 0 :(得分:0)

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

原因是您将keyindex一起使用。

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

要解决这个问题,您不应该依赖index中的key,而要使用其他一些可预测的,稳定的,毫无道理的值。您的情况是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]从下面的评论中提取了一些片段

  

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

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