react中key的另一面用法

当谈起react中key属性,首先且唯一能想到的就是map遍历生成元素时得加上的那么个东西,除此之外key似乎不会用在别的地方了——这是我们对key的印象:它很重要但存在感低。
key是否真的只有这么一个用法?此处按下不表,先来回顾下key的原理及作用:

作用:

key的作用主要是用来减少没必要的diff算法对比,因为对于一个组件或者节点来说,只要父节点状态或者属性发生变化,该组件就会进行diff对比,即使该组件没变化。而如果为组件引入了key值,就可以在diff对比前先做一个校验,如果组件加上了key值,react就会在渲染时对该组件的身份进行校验,首先校验新旧组件的key值是不是一致,不一致的话,该组件直接销毁,然后再新建该组件;如果一致,则比较组件的属性是否发生变化,如果发生变化,则采用diff算法进行对比,然后得出差异对象,如果属性没发生变化,则认为该组件不需要改变。
以上就是key在react中对组件渲染的影响。

运行原理如图:
image.png

再来讲一下背景

以上对key的剖析提到了两个关键词销毁新建,这个特性其实拥有很要紧的作用,在某些特定的场景可以利用到。
为什么会突然关注到key的这个特性?这是因为有一天我在对react官网的基础知识点做”温故而知新“时候的看到有一节讲到了 state 依赖 props 的情况下如何处理

image.png


这个截图官网链接是:https://zh-hans.reactjs.org/docs/react-component.html#constructor
截图中 【避免派生状态的博文】 链接中与本文主角 key 属性相关的地址是 :你可能不需要使用派生 state

它在这里提到了如下观点:

  • 尽量不要使用props派生到state的方式来传递数据

  • 不一定需要派生state:尝试一下 memoization?

  • 万不得已需要派生状态,也不要将this.state放在constructor中,因为constructor只会在生命周期中执行一次,无法响应后续props变化

  • 派生state后需要结合使用 componentWillReceivePropsgetDerivedStateFromProps控制 propsstate的修改,否则组件不随props变化而更新

  • 通过修改key以强制重置组件

官网中讲到了一个state依赖props而出现bug的场景,demo地址:点击查看。我描述如下:当我使用默认的同一个邮箱地址来登录不同的站点,在我编辑一个邮箱后我想切换到另一个站点,发现此时输入框中仍然保留了上次被编辑后的状态。很明显此时我期望的是输入框中应该重置为默认的那个邮箱地址。造成这个问题的原因是从one切换到two后UncontrolledEmailInput组件接收到的props.defaultEmail是一模一样的并没有发生改变,因此也就不会重置state。
官方对这个问题提出了两个解决方案:

  • 一个是将输入组件对其父组件受控,value和change事件都由父组件来传入,避免子组件state与props冲突
  • 另一个就是提到了本文主角使用 key 属性来销毁旧组件的方式

key的解决方案

使用key属性解决方案我将官方demo整理如下:

import React, { Component, Fragment } from "react";
import ReactDom  from "react-dom";
const fakeAccounts = [
  {
    id: 1,
    name: "One",
    email: "fake.email@example.com"
  },
  {
    id: 2,
    name: "Two",
    email: "fake.email@example.com"
  }
];

class UncontrolledEmailInput extends Component {
  state = {
    email: this.props.defaultEmail
  };
  handleChange = event => {
    this.setState({ email: event.target.value });
  };
  render() {
    return (
      <label>
        Email: <input onChange={this.handleChange} value={this.state.email} />
      </label>
    );
  }
}
class AccountsList extends Component {
  state = {
    selectedIndex: 0
  };
  render() {
    const { accounts } = this.props;
    const { selectedIndex } = this.state;
    const selectedAccount = accounts[selectedIndex];
    return (
      <Fragment>
        <h1>
          This demo illustrates resetting an uncontrolled component with a key
        </h1>
        <blockquote>First, make an edit to the account "One" email.</blockquote>
        <UncontrolledEmailInput
          key={selectedAccount.id}      // 关键之处,可删掉它来测试
          defaultEmail={selectedAccount.email}
        />
        <blockquote>Next, select account "Two" below.</blockquote>
        <p>
          Accounts:
          {this.props.accounts.map((account, index) => (
            <label key={account.id}>
              <input
                type="radio"
                name="account"
                checked={selectedIndex === index}
                onChange={() => this.setState({ selectedIndex: index })}
              />{" "}
              {account.name}
            </label>
          ))}
        </p>
      </Fragment>
    );
  }
}
ReactDom.render(
  <AccountsList accounts={fakeAccounts} />,
  document.getElementById("root")
);

它将UncontrolledEmailInput组件添加了key属性,利用key对组件渲染的特殊特性,当切换onetwo按钮时传入到UncontrolledEmailInput组件的key值发生变化,从而每次会销毁然后重建UncontrolledEmailInput组件,因而UncontrolledEmailInput组件中输入框的内容肯定都回到最初。
完美解决!仅仅一个很小的知识点就能解决这个小小的但又棘手的问题

本篇除了key,也让自己回顾了下派生state、不常用的生命周期如getDerivedStateFromPropsUNSAFE_componentWillReceiveProps、受控组件等等一些知识点,每次回顾老的知识点,总能发现很多知识又变得生疏了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容