Як працюють клавіші React та цікаві речі, які ви можете з ними робити

React використовує keyатрибут на етапі узгодження, щоб вирішити, які елементи можна використовувати повторно для наступного візуалізації. Вони важливі для динамічних списків. React порівняє ключі нового елемента з попередніми ключами та 1) змонтує компоненти, що мають новий ключ; 2) відключить компоненти, ключі яких більше не використовуються.

Багато розробників React чули загальну пораду, яку не слід використовувати indexяк ключ. Але що саме може піти не так, якщо неправильно використовувати keys? Що ще ми можемо зробити, коли ми бавимося клавішами?

Для кращого розуміння розглянемо приклад відображення списку inputs. При натисканні кнопки ми вставляємо новий елемент із текстом 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

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

    Інший цікавий момент - renderдзвінки, які трапляються. Елемент a PureComponent, тому він оновлюється лише тоді, коли textзмінюється властивість (або стан):

    rerendering Frontrerendering Firstrerendering SecondMounted Second

    Усі компоненти відтворені повторно. Це трапляється тому, що елемент з key: 0повторно використовується для першого елемента даних і отримує його props, але перший елемент даних тепер є новим Frontоб'єктом, що викликає a render. Те ж саме відбувається з іншими компонентами, оскільки старі елементи даних тепер усі переміщуються в одному місці.

    Отже, що виправити? Виправити це легко: ми надаємо кожному елементу даних списку унікальний idодноразовий момент створення (не на кожному візуалізації!) Екземпляри всіх компонентів будуть співставлені з відповідним елементом даних. Вони отримують те саме props, що і раніше, і це дозволяє уникнути іншого render.

    Давайте ігноруватимемо переваги продуктивності, які зараз отримує використання ids у динамічних списках. Приклад показує, що помилки, введені ключами, трапляються лише стосовно неконтрольованих компонентів , компонентів, що зберігають внутрішній стан .

    Якщо ми перепишемо Itemяк контрольований компонент, перемістивши стан із нього, помилка зникне.

    Чому? Знову ж таки, оскільки помилка повторно використовувала компонент для іншого елемента даних. Отже, внутрішній стан все ще відображав стан попереднього елемента даних , але реквізити іншого . Зробивши компонент контрольованим, повністю видаливши його стан, ми більше не маємо цієї розбіжності. (Але проблема з непотрібними повторними відтвореннями все ще є.)

    Зловживання ключами для виправлення непрацюючих сторонніх компонентів

    React потребує лише keys при збігу декількох елементів, тому встановлення ключа для однієї дитини не потрібно. Але все одно може бути корисно встановити ключ для одного дочірнього компонента.

    Якщо ви зміните ключ, React викине весь компонент (відключить його) і змонтує новий екземпляр компонента на його місці. Чому це може бути корисним?

    Знову ж таки, ми повертаємось до неконтрольованих компонентів . Іноді ви використовуєте сторонній компонент і не можете змінити його код, щоб зробити його керованим. Якщо компонент має якийсь внутрішній стан, і він реалізований погано (наприклад, стан виведено лише один раз у конструкторі, але getDerivedStateFromProps/ componentWillReceivePropsне реалізовано для відображення повторюваних propsзмін у його внутрішньому стані) , стандартний набір інструментів React не може вам допомогти тут. Немає forceRemount.

    Однак ми можемо просто встановити новий keyдля цього компонента, щоб досягти бажаної поведінки повної ініціалізації нового компонента. Старий компонент буде демонтовано, а новий буде змонтовано з новим propsініціалізацією state.

    TL; DR:

    Використовуючи indexяк ключ можна:

    1. призведуть до непотрібних рендерів
    2. вводити помилки, коли елементи списку є неконтрольованими компонентами, але все ще використовуютьсяprops

    keyВластивість може бути використано , щоб змусити повне монтування компонента, який іноді може бути корисно.

    Спочатку опубліковано на cmichel.io