React: 组件的key

参考文档:
React文档:Lists and Keys
React文档:Reconciliation
React 实践心得:key 属性的原理和用法 ——淘宝前端团队

A “key” is a special string attribute you need to include when creating lists of elements.

最早在React文档中看到关于组件的key,当时并没有很在意,直到某天在控制台看到了如下报错信息:

Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `App`. See https://fb.me/react-warning-keys for more information.

那么这个 key 究竟有多重要呢?

React文档中说应当在数组中给组件一个单独的 key ,React 用这个 key 作为组件的身份来识别具体对哪个组件做增删改各种操作。通常使用这个组件的唯一标识来作为key值,例如组件数据的id等:

const todoItems = todos.map((todo) =>
  
  • {todo.text}
  • );

    文档中写到,通常来说,不建议使用 index 作为 key 值。
    毕竟 index 太易变,如果使用 index 作为 key,某种程度上来说就失去了使用 key 的意义,同时可能还会引发其他的问题。但是,如果你在通过遍历数组生成一组组件时没有为每个组件分配key值,React 会自动使用 index 作为 key 值来分配给每个组件

    注意:

    • 关于 key 值的唯一性:

    Keys used within arrays should be unique among their siblings. However they don’t need to be globally unique.

    也就是说,我们可以在生成两组不同的组件时使用相同的一组 key。

    • key 只提供给 React 使用

    Keys serve as a hint to React but they don’t get passed to your components.

    const content = posts.map((post) =>
      
    )
    

    虽然 key 在使用时看起来和 props 很像,但你并不能在组件中通过 prop 取到它的值,如上述代码,在 Post 组件中,可以取到 props.id 的值,但 props.key 的值是拿不到的。

    看起来没有使用数组 map() 时,你的组件可能也需要 key

    认识到 key 的重要性之后,我也曾经天真地以为只需要在 map 中使用它,毕竟React文档好像也只是在强调 map,直到上一个某一天之后不太久的某天……

    这次,我遇到了一个略微复杂的需求,大概是要先 map 一个数据数组,加载一个用户列表:

    const userList = users.map((user) =>
      
    )
    

    加载用户列表的过程当然每个 item 都需要一个
    key 的,我选择用 user.id 作为 UserItem 的 key,然后在与某个 item 发生交互(例如点击)时,把 user.id 传给用户详情组件 ,拿着 user.id 去redux 里查询这个用户信息数据,如果没有就去后端请求这个数据,将后端返回的数据加入redux,再从 redux 中取得数据加载到组件,每次只展示当前点击的 UserItem 对应的 UserInfoCard,但所有的 UserInfo 组件都自动渲染在某一个节点(假设为Container)下。所有 UserInfoCard 一旦 mount,在父节点 Container 未发生变化时不会 unmount,仅会 update 数据和是否可见的属性。

    面对这个逻辑,作为 UserInfoCard 的使用者,我没有关心它具体渲染在哪个节点下,自然忽略了它们共有一个父节点,也就是说,虽然没有使用 map 来生成一组 UserInfo 组件,但确确实实每增加一个UserInfoCard,都等同于在它们的父节点Container下为它们增加了一个同为 UserInfoCard 的好姐妹。

    这样实际上和 map 生成的一组好兄弟本质上并没有什么区别,没有 key 值的话,React 难以对它们加以区分,另外本地没有的数据还需要从后端获取,假设这样一种情况:

    1. 点击一号用户的 UserItem,需向后端请求一号用户的数据
    2. 在后端的数据返回之前,点击二号用户的 UserItem,二号用户的数据也没有,向后端请求二号用户的数据

    从点击一号用户到再点击二号用户的过程,对父节点 Container 来说只是一个
    Update 的过程,在点击二号用户之后,收到一号用户的数据之前,对父节点来说,此时它有两个完全一样的React子元素(两个没有数据的 UserInfoCard ),此时收到了一个用户的信息数据,那么把它更新给哪个 UserInfoCard 呢?请求数据这种异步操作无法预料哪一条请求的数据会先到达,这就很容易造成混乱了——可能你点击了二号用户的UserItem,看到的却是一号用户的信息。

    为了避免这种混乱,使用 key 属性就非常有必要了。

    在使用 UserInfoCard 时将 user.id 作为
    key 一并传给 UserInfoCard,这样用户在点击不同的 UserItem 时,key 也发生了变化,结合前面谈到的 key 的作用,不难发现,对 React 来说此时就很容易分辨究竟是哪个 UserInfoCard 需要更新,即使多次点击同一个 UserItem,也不会创建多个相同的 UserInfoCard,而是会更新该 key 对应的 UserInfoCard 实例。

    总结:不止在 map 一个数组创建一组组件实例时需要使用 key 为每个数组元素标记,几乎在任何由统一结构或同一组件实例化为一组对象时,都应使用 key 作为它们的身份识别。

    参考文档里最后一篇文章提到了在拥有复杂状态(每个状态有具体与之对应的对象)的组件里使用 key 来使组件在适当的时候触发销毁和重建,以此来保持组件内部逻辑的清晰,这也是文档里没有提到的一种对 React key 的应用。

    你可能感兴趣的:(React: 组件的key)