react中key的另一面用法

當談起react中key屬性,首先且唯一能想到的就是map遍歷生成元素時得加上的那么個東西,除此之外key似乎不會用在別的地方了——這是我們對key的印象:它很重要但存在感低。
key是否真的只有這么一個用法偎痛?此處按下不表,先來回顧下key的原理及作用:

作用:

key的作用主要是用來減少沒必要的diff算法對比,因為對于一個組件或者節(jié)點來說塞颁,只要父節(jié)點狀態(tài)或者屬性發(fā)生變化,該組件就會進行diff對比吸耿,即使該組件沒變化祠锣。而如果為組件引入了key值,就可以在diff對比前先做一個校驗咽安,如果組件加上了key值伴网,react就會在渲染時對該組件的身份進行校驗,首先校驗新舊組件的key值是不是一致妆棒,不一致的話澡腾,該組件直接銷毀,然后再新建該組件糕珊;如果一致动分,則比較組件的屬性是否發(fā)生變化,如果發(fā)生變化红选,則采用diff算法進行對比澜公,然后得出差異對象,如果屬性沒發(fā)生變化纠脾,則認為該組件不需要改變玛瘸。
以上就是key在react中對組件渲染的影響。

運行原理如圖:
image.png

再來講一下背景

以上對key的剖析提到了兩個關(guān)鍵詞銷毀新建苟蹈,這個特性其實擁有很要緊的作用糊渊,在某些特定的場景可以利用到。
為什么會突然關(guān)注到key的這個特性慧脱?這是因為有一天我在對react官網(wǎng)的基礎(chǔ)知識點做”溫故而知新“時候的看到有一節(jié)講到了 state 依賴 props 的情況下如何處理渺绒。

image.png


這個截圖官網(wǎng)鏈接是:https://zh-hans.reactjs.org/docs/react-component.html#constructor
截圖中 【避免派生狀態(tài)的博文】 鏈接中與本文主角 key 屬性相關(guān)的地址是 :你可能不需要使用派生 state

它在這里提到了如下觀點:

  • 盡量不要使用props派生到state的方式來傳遞數(shù)據(jù)

  • 不一定需要派生state:嘗試一下 memoization?

  • 萬不得已需要派生狀態(tài)宗兼,也不要將this.state放在constructor中躏鱼,因為constructor只會在生命周期中執(zhí)行一次,無法響應(yīng)后續(xù)props變化

  • 派生state后需要結(jié)合使用 componentWillReceivePropsgetDerivedStateFromProps控制 propsstate的修改殷绍,否則組件不隨props變化而更新

  • 通過修改key以強制重置組件

官網(wǎng)中講到了一個state依賴props而出現(xiàn)bug的場景染苛,demo地址:點擊查看。我描述如下:當我使用默認的同一個郵箱地址來登錄不同的站點主到,在我編輯一個郵箱后我想切換到另一個站點茶行,發(fā)現(xiàn)此時輸入框中仍然保留了上次被編輯后的狀態(tài)。很明顯此時我期望的是輸入框中應(yīng)該重置為默認的那個郵箱地址登钥。造成這個問題的原因是從one切換到two后UncontrolledEmailInput組件接收到的props.defaultEmail是一模一樣的并沒有發(fā)生改變畔师,因此也就不會重置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}      // 關(guān)鍵之處,可刪掉它來測試
          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值發(fā)生變化,從而每次會銷毀然后重建UncontrolledEmailInput組件献幔,因而UncontrolledEmailInput組件中輸入框的內(nèi)容肯定都回到最初懂傀。
完美解決趾诗!僅僅一個很小的知識點就能解決這個小小的但又棘手的問題

本篇除了key蜡感,也讓自己回顧了下派生state、不常用的生命周期如getDerivedStateFromPropsUNSAFE_componentWillReceiveProps恃泪、受控組件等等一些知識點郑兴,每次回顧老的知識點,總發(fā)現(xiàn)很多知識又變得生疏了贝乎,這還真是個問題啊情连。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市览效,隨后出現(xiàn)的幾起案子却舀,更是在濱河造成了極大的恐慌,老刑警劉巖锤灿,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挽拔,死亡現(xiàn)場離奇詭異,居然都是意外死亡但校,警方通過查閱死者的電腦和手機螃诅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人术裸,你說我怎么就攤上這事倘是。” “怎么了袭艺?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵搀崭,是天一觀的道長。 經(jīng)常有香客問我猾编,道長门坷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任袍镀,我火速辦了婚禮默蚌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苇羡。我一直安慰自己绸吸,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布设江。 她就那樣靜靜地躺著锦茁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叉存。 梳的紋絲不亂的頭發(fā)上码俩,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音歼捏,去河邊找鬼稿存。 笑死,一個胖子當著我的面吹牛瞳秽,可吹牛的內(nèi)容都是我干的瓣履。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼练俐,長吁一口氣:“原來是場噩夢啊……” “哼袖迎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起腺晾,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤燕锥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后悯蝉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體归形,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年泉粉,在試婚紗的時候發(fā)現(xiàn)自己被綠了连霉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榴芳。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖跺撼,靈堂內(nèi)的尸體忽然破棺而出窟感,到底是詐尸還是另有隱情,我是刑警寧澤歉井,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布柿祈,位于F島的核電站,受9級特大地震影響哩至,放射性物質(zhì)發(fā)生泄漏躏嚎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一菩貌、第九天 我趴在偏房一處隱蔽的房頂上張望卢佣。 院中可真熱鬧,春花似錦箭阶、人聲如沸虚茶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘹叫。三九已至,卻和暖如春诈乒,著一層夾襖步出監(jiān)牢的瞬間罩扇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工怕磨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喂饥,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓癌压,卻偏偏與公主長得像仰泻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子滩届,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容