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

IT技术 css reactjs css-animations react-jsx
2021-04-07 13:23:15

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

这里有一个 jsbin 来说明这个问题尝试按“添加”按钮以验证插入动画确实按预期工作,然后单击任何项​​目旁边的“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}/>)
})

Reactkey在虚拟 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>

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

const arr = [2, 4, 8];

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

请注意,8现在驻留在索引中2React 看到这个 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}/>)
    })

    // ...
  }
}

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

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