React: 組件的key

參考文檔:
React文檔:Lists and Keys
React文檔:Reconciliation
React 實踐心得:key 屬性的原理和用法 ——淘寶前端團(tuán)隊

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

最早在React文檔中看到關(guān)于組件的key,當(dāng)時并沒有很在意逃延,直到某天在控制臺看到了如下報錯信息:

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文檔中說應(yīng)當(dāng)在數(shù)組中給組件一個單獨的 key 贺氓,React 用這個 key 作為組件的身份來識別具體對哪個組件做增刪改各種操作蹲缠。通常使用這個組件的唯一標(biāo)識來作為key值仗扬,例如組件數(shù)據(jù)的id等:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

文檔中寫到斋射,通常來說育勺,不建議使用 index 作為 key 值。
畢竟 index 太易變罗岖,如果使用 index 作為 key涧至,某種程度上來說就失去了使用 key 的意義,同時可能還會引發(fā)其他的問題桑包。但是南蓬,如果你在通過遍歷數(shù)組生成一組組件時沒有為每個組件分配key值,React 會自動使用 index 作為 key 值來分配給每個組件哑了。

注意:

  • 關(guān)于 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 的值是拿不到的涂圆。

看起來沒有使用數(shù)組 map() 時,你的組件可能也需要 key

認(rèn)識到 key 的重要性之后优妙,我也曾經(jīng)天真地以為只需要在 map 中使用它乘综,畢竟React文檔好像也只是在強(qiáng)調(diào) map,直到上一個某一天之后不太久的某天……

這次套硼,我遇到了一個略微復(fù)雜的需求卡辰,大概是要先 map 一個數(shù)據(jù)數(shù)組,加載一個用戶列表:

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

加載用戶列表的過程當(dāng)然每個 item 都需要一個
key 的邪意,我選擇用 user.id 作為 UserItem 的 key九妈,然后在與某個 item 發(fā)生交互(例如點擊)時,把 user.id 傳給用戶詳情組件 <UserInfoCard id={user.id}/>雾鬼,拿著 user.id 去redux 里查詢這個用戶信息數(shù)據(jù)萌朱,如果沒有就去后端請求這個數(shù)據(jù),將后端返回的數(shù)據(jù)加入redux策菜,再從 redux 中取得數(shù)據(jù)加載到組件晶疼,每次只展示當(dāng)前點擊的 UserItem 對應(yīng)的 UserInfoCard,但所有的 UserInfo 組件都自動渲染在某一個節(jié)點(假設(shè)為Container)下又憨。所有 UserInfoCard 一旦 mount翠霍,在父節(jié)點 Container 未發(fā)生變化時不會 unmount蠢莺,僅會 update 數(shù)據(jù)和是否可見的屬性。

面對這個邏輯躏将,作為 UserInfoCard 的使用者锄弱,我沒有關(guān)心它具體渲染在哪個節(jié)點下祸憋,自然忽略了它們共有一個父節(jié)點,也就是說夺衍,雖然沒有使用 map 來生成一組 UserInfo 組件,但確確實實每增加一個UserInfoCard沟沙,都等同于在它們的父節(jié)點Container下為它們增加了一個同為 UserInfoCard 的好姐妹河劝。

這樣實際上和 map 生成的一組好兄弟本質(zhì)上并沒有什么區(qū)別矛紫,沒有 key 值的話,React 難以對它們加以區(qū)分颊咬,另外本地沒有的數(shù)據(jù)還需要從后端獲取牡辽,假設(shè)這樣一種情況:

  1. 點擊一號用戶的 UserItem,需向后端請求一號用戶的數(shù)據(jù)
  2. 在后端的數(shù)據(jù)返回之前敞临,點擊二號用戶的 UserItem,二號用戶的數(shù)據(jù)也沒有挺尿,向后端請求二號用戶的數(shù)據(jù)

從點擊一號用戶到再點擊二號用戶的過程,對父節(jié)點 Container 來說只是一個
Update 的過程编矾,在點擊二號用戶之后,收到一號用戶的數(shù)據(jù)之前窄俏,對父節(jié)點來說蹂匹,此時它有兩個完全一樣的React子元素(兩個沒有數(shù)據(jù)的 UserInfoCard )凹蜈,此時收到了一個用戶的信息數(shù)據(jù)限寞,那么把它更新給哪個 UserInfoCard 呢?請求數(shù)據(jù)這種異步操作無法預(yù)料哪一條請求的數(shù)據(jù)會先到達(dá)仰坦,這就很容易造成混亂了——可能你點擊了二號用戶的UserItem昆烁,看到的卻是一號用戶的信息。

為了避免這種混亂缎岗,使用 key 屬性就非常有必要了。

在使用 UserInfoCard 時將 user.id 作為
key 一并傳給 UserInfoCard白粉,這樣用戶在點擊不同的 UserItem 時传泊,key 也發(fā)生了變化,結(jié)合前面談到的 key 的作用鸭巴,不難發(fā)現(xiàn)眷细,對 React 來說此時就很容易分辨究竟是哪個 UserInfoCard 需要更新,即使多次點擊同一個 UserItem鹃祖,也不會創(chuàng)建多個相同的 UserInfoCard溪椎,而是會更新該 key 對應(yīng)的 UserInfoCard 實例。

總結(jié):不止在 map 一個數(shù)組創(chuàng)建一組組件實例時需要使用 key 為每個數(shù)組元素標(biāo)記恬口,幾乎在任何由統(tǒng)一結(jié)構(gòu)或同一組件實例化為一組對象時校读,都應(yīng)使用 key 作為它們的身份識別。

參考文檔里最后一篇文章提到了在擁有復(fù)雜狀態(tài)(每個狀態(tài)有具體與之對應(yīng)的對象)的組件里使用 key 來使組件在適當(dāng)?shù)臅r候觸發(fā)銷毀和重建祖能,以此來保持組件內(nèi)部邏輯的清晰歉秫,這也是文檔里沒有提到的一種對 React key 的應(yīng)用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末养铸,一起剝皮案震驚了整個濱河市雁芙,隨后出現(xiàn)的幾起案子轧膘,更是在濱河造成了極大的恐慌,老刑警劉巖兔甘,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谎碍,死亡現(xiàn)場離奇詭異,居然都是意外死亡洞焙,警方通過查閱死者的電腦和手機(jī)蟆淀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闽晦,“玉大人扳碍,你說我怎么就攤上這事∠沈龋” “怎么了笋敞?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荠瘪。 經(jīng)常有香客問我夯巷,道長,這世上最難降的妖魔是什么哀墓? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任趁餐,我火速辦了婚禮,結(jié)果婚禮上篮绰,老公的妹妹穿的比我還像新娘后雷。我一直安慰自己吠各,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布候学。 她就那樣靜靜地躺著纵散,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掰茶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天符匾,我揣著相機(jī)與錄音啊胶,去河邊找鬼。 笑死焰坪,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的儒恋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼诫尽,長吁一口氣:“原來是場噩夢啊……” “哼牧嫉!你這毒婦竟也來了减途?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辽剧,失蹤者是張志新(化名)和其女友劉穎税产,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辟拷,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡梧兼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年羽杰,在試婚紗的時候發(fā)現(xiàn)自己被綠了到推。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡颜骤,死狀恐怖捣卤,靈堂內(nèi)的尸體忽然破棺而出八孝,到底是詐尸還是另有隱情鸠项,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布楼入,位于F島的核電站牧抽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏阐肤。R本人自食惡果不足惜呼巴,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诊赊。 院中可真熱鬧府瞄,春花似錦、人聲如沸遵馆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽换况。三九已至,卻和暖如春戈二,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腾供。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留节值,地道東北人黎侈。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像贴汪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子扳埂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

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