移除一个项目会导致 React 移除最后一个 DOM 节点,而不是与该项目关联的那个

Posted

技术标签:

【中文标题】移除一个项目会导致 React 移除最后一个 DOM 节点,而不是与该项目关联的那个【英文标题】:Removing an item causes React to remove the last DOM node instead of the one associated with that item 【发布时间】:2015-08-05 01:45:44 【问题描述】:

我尝试使用 ReactCSSTransitionGroup 为列表插入和删除设置动画,但删除动画始终只为列表的最后一项而不是被删除的项设置动画。

Here's a jsbin to illustrate this problem。尝试按“添加”按钮以验证插入动画确实按预期工作,然后单击任何项​​目旁边的“x”以查看列表的最后一项动画而不是您尝试删除的动画的问题。

是我在设置 TransitionGroup 时做错了什么,还是我在 CSS 过渡定义中遗漏了什么?

【问题讨论】:

【参考方案1】:

您遇到此问题是因为您使用index 作为您的密钥:

let nodes = items.map((item, index) => 
  let idx = index
  return (<Item key=index value=item index=index _delete=this._onDelete/>)
)

React 在虚拟 DOM 比较期间使用 key 属性来确定删除了哪个元素,但索引永远无法充分满足此目的。

考虑这个例子:你从下面的数组开始,它会产生下面的 DOM 结构:

const arr = [2, 4, 6, 8];

<li key=0>2</li>
<li key=1>4</li>
<li key=2>6</li>
<li key=3>8</li>

然后假设您删除了索引2 处的元素。您现在拥有以下数组和以下 DOM 结构:

const arr = [2, 4, 8];

<li key=0>2</li>
<li key=1>4</li>
<li key=2>8</li>

注意8 现在驻留在索引2 中; React 看到这个 DOM 结构和最后一个的区别在于缺少了带有键 3li,因此将其删除。因此,无论您删除了哪个数组元素,生成的 DOM 结构都将缺少带有键 3li

解决方案是为列表中的每个项目使用唯一标识符;在现实生活中的应用程序中,您可能需要使用 id 字段或其他一些主键;对于像这样的应用,您可以生成一个递增的 ID:

let id = 0;
class List extends Component 
  constructor() 
    this.state = 
      items: [id: ++id, value: 1, id: ++id, value: 2]
    

    // ...
  

  _onClick(e) 
    this.state.items.push(id: ++id, value: Math.round(Math.random() * 10))
    this.setState(items: this.state.items)
  

  // ...

  render() 
    let items = this.state.items
    let nodes = items.map((item, index) => 
      let idx = index
      return (<Item key=item.id value=item.value index=index _delete=this._onDelete/>)
    )

    // ...
  

工作示例:http://jsbin.com/higofuhuni/2/edit

【讨论】:

啊,你是对的!不敢相信我错过了...我非常感谢您为我如此清楚地解释解决方案所付出的努力!我正在构建一个显示上传文件列表的组件,因此即使它们没有 id,我也可以像您描述的那样为它们分配唯一的临时 id(当列表项更改时不会更改)。再次感谢您! @Michelle Tilley:我无法在 jsbin 中看到输出。你能帮我得到输出吗,因为我不清楚上面的编码副概念

以上是关于移除一个项目会导致 React 移除最后一个 DOM 节点,而不是与该项目关联的那个的主要内容,如果未能解决你的问题,请参考以下文章

React defaultValue 导致过滤功能的行为与预期不同

React Native 问题:WebView 已从 React Native 中移除

vue项目监听滑块并移除。

CALayer 帧值即使在从超级层中移除后也会导致动画

C# 只移除最后一个字符

C# 只移除最后一个字符