【问题标题】:Deleting Stateful element from ReactJS array从 ReactJS 数组中删除有状态元素
【发布时间】:2015-02-16 11:44:32
【问题描述】:

我在删除 React 数组中的有状态元素时遇到问题。在下面的例子中,如果你点击第二个元素('B')上的'toggle',你会看到:

条目 id:0 名称:A 是活动切换删除

条目 ID:1 名称:B 未激活切换删除

条目 id:2 名称:C 是活动切换删除

如果您现在在第一个元素 ('A') 上单击“删除”:

条目 id:0 名称:B 是活动切换删除

条目 id:1 名称:C 未激活切换删除

B 发生了变化,但我想做的只是删除 A 而不会影响任何其他元素。根据文档,我有一个key,并尝试使用带有字符串前缀的键,但没有帮助。

有没有办法做到这一点而不将状态推送到TestContainer? (在我的实际情况中,我有很多不同类型的不同元素)。

<div id="test"></div>

<script type="text/jsx">

  var TestLine = React.createClass({
      getInitialState: function() {
          return {active: true};
      },
      handleLineRemove: function(e) {
        this.props.processLineRemove(this.props.id);
      },
      toggleActive: function(e) {
        this.setState({active: !this.state.active};
      },
      render: function() {
        return (
          <p>
            Entry id: {this.props.id} name: {this.props.name}
            {this.state.active ? " Is Active " : " Is Not Active " }
            <a onClick={this.toggleActive}>Toggle</a>
            &nbsp;<a onClick={this.handleLineRemove}>Remove</a>
          </p>
        );
      }
    });

     var TestContainer = React.createClass({
        getInitialState: function() {
          return {data: {lines: ['A','B','C']}};
        },
        processLineRemove: function(index) {
              var new_lines = this.state.data.lines.slice(0);
              new_lines.splice(index,1);
              var newState = React.addons.update(this.state.data, {
                lines: {$set: new_lines}
              });
          this.setState({data: newState});
        },
        render: function() {
            var body =[];
            for (var i = 0; i < this.state.data.lines.length; i++) {
                body.push(<TestLine 
                          key={i}
                          id={i}
                          name={this.state.data.lines[i]}
                          processLineRemove={this.processLineRemove} />)
            }
            return (
                <div>{body}</div>
            );
        }
    });

React.render(<TestContainer />, document.getElementById('test'));
</script>

【问题讨论】:

  • 总是需要某个集中位置来通知删除。您可以使用操作/调度程序来通知更改并将其传播回来,但从概念上讲,它与您现有的实现没有太大区别,如图所示。您需要通知拥有数据的任何组件或商店。
  • 元素正在通过拥有该数组的 TestContainer 成功删除。但是,剩余的数组元素没有正确重新渲染;只是名称标签正在移动,并且 TestLine 中的私有状态没有“移动”到数组中的新位置。
  • 那么您要问两个问题——如何在不涉及TestContainer 的情况下管理状态以及如何正确重新渲染。看起来答案应该对您有所帮助。

标签: javascript reactjs


【解决方案1】:

当您有一个包含任意删除/插入的列表时,您不能使用索引作为键。在这种情况下,您可以使用实际值,因为它没有改变。

var body = this.state.data.map(function(x, i){
    return <TestLine 
        key={x}
        name={x}
        processLineRemove={this.processLineRemove.bind(null, i)} />
}.bind(this));

在更复杂的情况下,您需要一个对象数组,并为每个对象分配一个具有唯一值的属性。假设状态为[makeItem('A'), makeItem('B'), ...]

function unique(){ return ++unique.i };
unique.i = 0;

function makeItem(text){ return {text: text, id: unique()}; };

var body = this.state.data.map(function(x, i){
    return <TestLine 
        key={x.id}
        name={x.text}
        processLineRemove={this.processLineRemove.bind(null, i)} />
}.bind(this));

【讨论】:

    【解决方案2】:

    如果您有一个项目列表并且您知道项目将被随机编辑,我不会创建这样的键。我宁愿在列表更改时创建唯一的键和 will not overlap 与其他键。像this 这样的简单随机密钥生成器就足够了。

    为什么?让我们回顾一下每个步骤。

    第一次渲染看起来像这样

    条目 id:0 名称:A 是活动切换删除

    条目 ID:1 名称:B 未激活切换删除

    条目 id:2 名称:C 是活动切换删除

    第二个看起来像这样

    条目 id:0 名称:B 未激活切换删除

    条目 id:1 名称:C 是活动切换删除

    现在是 React 采用这两个的时候了,并找出发生了什么变化。它将比较组件和键。

    它查看第一个元素并了解“活动”标志根据键设置为 true。因此,它将同样适用于在第二个渲染中找到的键 0。其余元素依此类推。

    因此,如果您的键在更改时不重叠,则可以避免此问题。 这是 AFAIK 的唯一方法。希望这会有所帮助。

    这是一个DEMO 解释上述内容。

    Courtesy

    【讨论】:

    • UUID 解决了许多服务器每秒创建多达数十亿个 uuid 且冲突几率几乎为 0% 的问题。这里有点矫枉过正。
    • @FakeRainBrigand lodash.com/docs#uniqueId 会更有意义,例如(带前缀?)
    猜你喜欢
    • 2018-06-01
    • 2015-07-16
    • 2015-06-14
    • 2021-06-17
    • 2021-06-08
    • 2021-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多