移除一个项目会导致 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 结构和最后一个的区别在于缺少了带有键 3
的 li
,因此将其删除。因此,无论您删除了哪个数组元素,生成的 DOM 结构都将缺少带有键 3
的 li
。
解决方案是为列表中的每个项目使用唯一标识符;在现实生活中的应用程序中,您可能需要使用 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 导致过滤功能的行为与预期不同