React中的key值

2019年12月06日Web前端

在React中,jsx的循环是很常见的,但加入在循环中不加入唯一的key值,则会有以下的警告:Warning: Each child in a list should have a unique "key" prop。那不加key和加key,对于React又有什么区别呢?而且,我们经常也被告知不推荐用与数据无直接关联的index(序号)来做key值。

不带key值

我们先map生成一些没有key的值,再通过事件修改其值。最后以DOMNodeRemoved事件是否触发来判断diff结果。

class IndexPage extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      arr: ['a', 'b', 'c', 'd']
    }
  }

  componentDidMount() {
    // 第一个元素
    document.getElementById('a0').addEventListener('DOMNodeRemoved', function() {
      alert()
    })
    // 第四个元素
    document.getElementById('a3').addEventListener('DOMNodeRemoved', function() {
      alert()
    })
  }

  change = () => {
    const a = this.state.arr
    // 新增一个数据
    a.push('e')
    // 修改老的数据
    a[3] = 'D'

    this.setState({
      arr: a
    })
  }

  render() {
    return (
      <div>
        {
          // 不带key的循环
          this.state.arr.map((item, ind) => {
            return <span id={ `a${ind}` }>{ item }</span>
          })
        }
        <div>
          <button onClick={ this.change }>change</button>
        </div>
      </div>
    )
  }
}

页面上显示由 abcd 变成了 abcDe ,但是DOM移除事件并没有触发,diff起效果了。也就是说尽管没有key值,但是React仍对这些数据做了diff,将需要更新的节点的内容替换。

带key值

略微修改以上代码,增加key值,每次以数组的index为key值,并在按钮事件触发之后给index加了前置量temp。

class IndexPage extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      arr: ['a', 'b', 'c', 'd'],
      temp: 0
    }
  }

  componentDidMount() {
    // 第一个元素,此元素在第二次时会更换id值
    document.getElementById('a0').addEventListener('DOMNodeRemoved', function() {
      alert('a0')
    })
    // 第一个元素,此元素在第二次时不会更换id值
    document.getElementById('a3').addEventListener('DOMNodeRemoved', function() {
      alert('a3')
    })
  }

  change = () => {
    this.setState({
      temp: 2
    })
  }

  render() {
    return (
      <div>
        {
          this.state.arr.map((item, ind) => {
            return <span id={ `a${ind}` } key={ ind + this.state.temp }>{ item }</span>
          })
        }
        <div>
          <button onClick={ this.change }>change</button>
        </div>
      </div>
    )
  }
}

按钮点击后,只弹出了a0。可是在实际过程中,数组中的值并没有改变,却销毁了key值改变了DOM。这也是不推荐使用index做key值的一大原因,而是建议与数据有较大关联性的值。

key值的作用

是不是渐渐的理解key的作用了呢?简单的概括如下图:

key值的作用

文字概括如下:

  • 当存在key值,若key值改变,则直接销毁DOM,重新创建,若key没有改变,则检查属性是否需要更新。
  • 当不存在key值,直接diff处理。