删除一项会导致 React 删除最后一个 DOM 节点,而不是与该项目关联的节点
Removing an item causes React to remove the last DOM node instead of the one associated with that item
我试图用 ReactCSSTransitionGroup 为列表的插入和移除设置动画,但移除动画总是只为列表的最后一项设置动画,而不是正在删除的那个。
Here's a jsbin to illustrate this problem。尝试按 "Add" 按钮以验证插入动画确实按预期工作,然后单击任何项目旁边的 "x" 以查看列表的最后一项动画而不是您的动画的问题试图删除。
是我在设置 TransitionGroup 时做错了什么,还是我在 CSS 转换定义中遗漏了什么?
您遇到此问题是因为您使用 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}/>)
})
// ...
}
}
我试图用 ReactCSSTransitionGroup 为列表的插入和移除设置动画,但移除动画总是只为列表的最后一项设置动画,而不是正在删除的那个。
Here's a jsbin to illustrate this problem。尝试按 "Add" 按钮以验证插入动画确实按预期工作,然后单击任何项目旁边的 "x" 以查看列表的最后一项动画而不是您的动画的问题试图删除。
是我在设置 TransitionGroup 时做错了什么,还是我在 CSS 转换定义中遗漏了什么?
您遇到此问题是因为您使用 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}/>)
})
// ...
}
}