
React використовує key
атрибут на етапі узгодження, щоб вирішити, які елементи можна використовувати повторно для наступного візуалізації. Вони важливі для динамічних списків. React порівняє ключі нового елемента з попередніми ключами та 1) змонтує компоненти, що мають новий ключ; 2) відключить компоненти, ключі яких більше не використовуються.
Багато розробників React чули загальну пораду, яку не слід використовувати index
як ключ. Але що саме може піти не так, якщо неправильно використовувати key
s? Що ще ми можемо зробити, коли ми бавимося клавішами?
Для кращого розуміння розглянемо приклад відображення списку input
s. При натисканні кнопки ми вставляємо новий елемент із текстом Front
у передню частину списку.
import React from "react";import { render } from "react-dom";class Item extends React.PureComponent { state = { text: this.props.text }; onChange = event => { this.setState({ text: event.target.value }); }; componentDidMount() { console.log("Mounted ", this.props.text); } componentWillUnmount() { console.log("Unmounting ", this.props.text); } render() { console.log("rerendering ", this.props.text); const { text } = this.state; return ( ); }}class App extends React.Component { state = { items: [ { text: "First", id: 1 }, { text: "Second", id: 2 } ] }; addItem = () => { const items = [{ text: "Front", id: Date.now() }, ...this.state.items]; this.setState({ items }); }; render() { return ( {this.state.items.map((item, index) => ( ))}
Add Item ); }}render(, document.getElementById("root"));
Якщо ви використовуєте index
як ключ, трапляється таке:
CodeSandbox
CodeSandbox - це онлайн-редактор, розроблений для веб-додатків. codesandbox.io

Що робити, якщо в кінці списку буде вставлено інший Item
текст, Second
а не текст ? Ось що відбувається:Front
Item is an uncontrolled component
: Текст, який користувач записує у своєinput
поле, зберігається якstate
- Новий елемент даних
{ text: "Front" }
вставляється на початок даних списку. - Список повторно відтворюється із значенням індексу як
key
. Отже, попередні компоненти повторно використовуються для перших двох елементів даних і отримують правильний пропсFront
таFirst
, але стан не оновлюється вItem
. Ось чому перші два примірники компонентів зберігають однаковий текст. - Створено новий екземпляр компонента,
key: 2
оскільки попередній відповідний ключ не знайдено. Він заповнюєтьсяprops
останнім елементом даних списку, який єSecond
.

Інший цікавий момент - render
дзвінки, які трапляються. Елемент a PureComponent
, тому він оновлюється лише тоді, коли text
змінюється властивість (або стан):
rerendering Frontrerendering Firstrerendering SecondMounted Second
Усі компоненти відтворені повторно. Це трапляється тому, що елемент з key: 0
повторно використовується для першого елемента даних і отримує його props
, але перший елемент даних тепер є новим Front
об'єктом, що викликає a render
. Те ж саме відбувається з іншими компонентами, оскільки старі елементи даних тепер усі переміщуються в одному місці.
Отже, що виправити? Виправити це легко: ми надаємо кожному елементу даних списку унікальний id
одноразовий момент створення (не на кожному візуалізації!) Екземпляри всіх компонентів будуть співставлені з відповідним елементом даних. Вони отримують те саме props
, що і раніше, і це дозволяє уникнути іншого render
.
Давайте ігноруватимемо переваги продуктивності, які зараз отримує використання id
s у динамічних списках. Приклад показує, що помилки, введені ключами, трапляються лише стосовно неконтрольованих компонентів , компонентів, що зберігають внутрішній стан .
Якщо ми перепишемо Item
як контрольований компонент, перемістивши стан із нього, помилка зникне.
Чому? Знову ж таки, оскільки помилка повторно використовувала компонент для іншого елемента даних. Отже, внутрішній стан все ще відображав стан попереднього елемента даних , але реквізити іншого . Зробивши компонент контрольованим, повністю видаливши його стан, ми більше не маємо цієї розбіжності. (Але проблема з непотрібними повторними відтвореннями все ще є.)
Зловживання ключами для виправлення непрацюючих сторонніх компонентів
React потребує лише key
s при збігу декількох елементів, тому встановлення ключа для однієї дитини не потрібно. Але все одно може бути корисно встановити ключ для одного дочірнього компонента.
Якщо ви зміните ключ, React викине весь компонент (відключить його) і змонтує новий екземпляр компонента на його місці. Чому це може бути корисним?
Знову ж таки, ми повертаємось до неконтрольованих компонентів . Іноді ви використовуєте сторонній компонент і не можете змінити його код, щоб зробити його керованим. Якщо компонент має якийсь внутрішній стан, і він реалізований погано (наприклад, стан виведено лише один раз у конструкторі, але getDerivedStateFromProps
/ componentWillReceiveProps
не реалізовано для відображення повторюваних props
змін у його внутрішньому стані) , стандартний набір інструментів React не може вам допомогти тут. Немає forceRemount
.
Однак ми можемо просто встановити новий key
для цього компонента, щоб досягти бажаної поведінки повної ініціалізації нового компонента. Старий компонент буде демонтовано, а новий буде змонтовано з новим props
ініціалізацією state
.
TL; DR:
Використовуючи index
як ключ можна:
- призведуть до непотрібних рендерів
- вводити помилки, коли елементи списку є неконтрольованими компонентами, але все ще використовуються
props
key
Властивість може бути використано , щоб змусити повне монтування компонента, який іноді може бути корисно.
Спочатку опубліковано на cmichel.io
