美文网首页
React: 组件的key

React: 组件的key

作者: VirtualX | 来源:发表于2017-11-28 00:20 被阅读0次

    参考文档:
    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) =>
      <li key={todo.id}>
        {todo.text}
      </li>
    );
    

    文档中写到,通常来说,不建议使用 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) =>
      <Post
        key={post.id}
        id={post.id}
        title={post.title} />
    )
    

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

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

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

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

    const userList = users.map((user) =>
      <UserItem
        key={user.id}
        id={user.id} />
    )
    

    加载用户列表的过程当然每个 item 都需要一个
    key 的,我选择用 user.id 作为 UserItem 的 key,然后在与某个 item 发生交互(例如点击)时,把 user.id 传给用户详情组件 <UserInfoCard id={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

          本文链接:https://www.haomeiwen.com/subject/rupdbxtx.html