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

use*_*412 14 css3 css-animations reactjs react-jsx

我试图使用ReactCSSTransitionGroup为列表插入和删除设置动画,但删除动画始终仅动画列表的最后一项而不是正在删除的项.

这是一个jsbin来说明这个问题.尝试按"添加"按钮以验证插入动画确实按预期工作,然后单击任何项​​目旁边的"x"以查看列表的最后一项是动画而不是您尝试删除的问题.

在设置TransitionGroup时我做错了什么,或者我在CSS转换定义中遗漏了什么?

Mic*_*ley 22

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

let nodes = items.map((item, index) => {
  let idx = index
  return (<Item key={index} value={item} index={index} _delete={this._onDelete}/>)
})
Run Code Online (Sandbox Code Playgroud)

React key在虚拟DOM diffing期间使用该属性来确定删除了哪个元素,但索引永远不会充分满足此目的.

考虑这个例子:你从以下数组开始,这导致以下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>
Run Code Online (Sandbox Code Playgroud)

然后想象你删除索引处的元素2.您现在拥有以下数组和以下DOM结构:

const arr = [2, 4, 8];

<li key={0}>2</li>
<li key={1}>4</li>
<li key={2}>8</li>
Run Code Online (Sandbox Code Playgroud)

请注意,8现在位于索引中2; React看到这个DOM结构和最后一个结构之间的区别在于缺少liwith键3,因此它将其删除.因此,无论您删除哪个数组元素,生成的DOM结构都将缺少liwith键3.

解决方案是为列表中的每个项目使用唯一标识符; 在现实生活中,您可能需要使用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}/>)
    })

    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • @Michelle Tilley:我无法在jsbin中看到输出。您是否可以帮助我获得输出,因为我在编码副中对上述概念不清楚 (3认同)