ReactJS:组件列表的错误呈现

时间:2018-10-25 20:04:18

标签: reactjs dictionary constructor components

我有一个组件(“ ComponentA”),该组件在另一个组件(“ ComponentB”)的render()内部的map()中多次渲染。

此外,我在ComponentB中有一个“ ADD”按钮,单击该按钮时(通过'unshift()方法)在其ComponentB的“ state.objects”数组(用于映射ComponentA实例)中预先放置一个对象。每个前置对象都有一个属性'prop1',我用它来设置ComponentA的输入文本值。

问题是我在ComponentA的每个实例的状态下都没有得到期望值:在所有情况下,该元素始终具有值“ 1”(我希望... 3、2、1)。

此外,我看到ComponentA的构造函数在每个映射循环中仅被调用一次,尤其是在最后一个实例的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');
        this.state = { details: props.details };
      }
      render() {
        console.log('ComponentA render() called, prop1 value is ' + this.state.details.prop1);
        return (
          <input type="text" value={this.state.details.prop1}></input>
        )
      }
    }

因此,使用上面的代码,单击添加按钮一次,将记录以下内容:

    ComponentA constructor called
    ComponentA render() called, prop1 value is 1

第二次单击按钮将记录:

    ComponentA render() called, prop1 value is 1
    ComponentA constructor called'
    ComponentA render() called, prop1 value is 1

第三次单击按钮将记录:

    ComponentA render() called, prop1 value is 1
    ComponentA render() called, prop1 value is 1
    ComponentA constructor called'
    ComponentA render() called, prop1 value is 1

...等等。

在ComponentA的所有实例中,输入文本值为“ 1”。

我的问题是:

1)我如何对其进行编码以获得所需的ComponentA递增值?

2)为什么在该特定位置(在最后一个渲染实例之前)仅一次调用了映射组件的构造函数?

注意: 上面的代码只是我实际代码的简化版本,仅显示了演示该问题的必要部分。

2 个答案:

答案 0 :(得分:0)

您通过进行类似this.state.objects.unshift()的操作来should never mutate the state directly-改用this.setState()。当您直接在this.state内修改数组或对象时,React不知道其中的某些值已更改。这回答了您的两个问题。因此,代替直接修改this.state.objects

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

您应该以不变的方式在数组前添加新项目:

const newItem = { prop1: this.state.objects.length + 1 };
this.setState({
  objects: [newItem].concat(this.state.objects)
});

除此之外,您忘记在super(props)ComponentA的构造函数中调用ComponentB。另外,无需将传递的道具复制到ComponentA内部的组件状态中-只需使用道具即可。您可以看到有效的代码和框here

答案 1 :(得分:0)

此问题的修复来自使用以下代码:

    <input value={this.props....} onChange={...} // first version

代替

    <input value={this.state....} onChange={....} // second version

我以前认为第二个版本是正确的,并且第一个版本阻止了输入的可编辑性。但是似乎onChange上的存在使第一个版本正常工作(正确显示了初始值和编辑后的值,并允许进行编辑)