Removing an item causes React to remove the last DOM node instead of the one associated with that item

后端 未结 1 1152
轮回少年
轮回少年 2020-12-05 15:11

I was trying to animate list insertion and removal with ReactCSSTransitionGroup, but the removal animation always animates only the last item of the list instead of the one

相关标签:
1条回答
  • 2020-12-05 15:44

    You're experiencing this problem because you're using index as your key:

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

    React uses the key property during virtual DOM diffing to figure out which element was removed, but indexes will never serve this purpose sufficiently.

    Consider this example: you start off with the following array, which results in the following DOM structure:

    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>
    

    Then imagine you remove the element at index 2. You now have the following array, and the following DOM structure:

    const arr = [2, 4, 8];
    
    <li key={0}>2</li>
    <li key={1}>4</li>
    <li key={2}>8</li>
    

    Notice that the 8 now resides in index 2; React sees that the difference between this DOM structure and the last one is that the li with key 3 is missing, so it removes it. So, no matter which array element you removed, the resulting DOM structure will be missing the li with key 3.

    The solution is to use a unique identifier for each item in the list; in a real-life application, you might have an id field or some other primary key to use; for an app like this one, you can generate a incrementing 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}/>)
        })
    
        // ...
      }
    }
    

    Working example: http://jsbin.com/higofuhuni/2/edit

    0 讨论(0)
提交回复
热议问题