Компонент

Ми рекомендуємо визначати компоненти як функції замість класів. Дивіться, як мігрувати.

Component є базовим класом для React-компонентів, визначених як JavaScript-класи. Компоненти класу все ще підтримуються React, але ми не рекомендуємо використовувати їх у новому коді.

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

Довідник

Component

Щоб визначити React-компонент як клас, розширьте вбудований клас Component та визначте метод render:

import { Component } from 'react';

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

Тільки метод render є обов'язковим, інші методи необов'язкові.

Дивіться більше прикладів нижче.


контекст

Контекст компонента класу доступний як this.context. Він доступний лише якщо ви вкажете який контекст ви хочете отримати за допомогою static contextType (сучасний) або static contextTypes (застарілий).

Компонент класу може читати лише один контекст за раз.

class Button extends Component {
  static contextType = ThemeContext;

  render() {
    const theme = this.context;
    const className = 'button-' + theme;
    return (
      <button className={className}>
        {this.props.children}
      </button>
    );
  }
}

Читання this.context у компонентах класу еквівалентне useContext у компонентах функції.

Дивіться, як мігрувати.


пропси

Пропси, передані компоненту класу, доступні як this.props.

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

<Greeting name="Taylor" />

Читання this.props у компонентах класів еквівалентно оголошенню пропсів у компонентах функцій.

Дивіться, як перенести.


refs

Цей API буде видалено в наступному старшому релізі React. Використовуйте createRef замість нього.

Дозволяє отримати доступ до застарілих рядкових посилань для цього компонента.


state

Стан компонента класу доступний як this.state. Поле state має бути об'єктом. Не змінюйте стан безпосередньо. Якщо ви хочете змінити стан, викличте setState з новим станом.

class Counter extends Component {
  state = {
    age: 42,
  };

  handleAgeChange = () => {
    this.setState({
      age: this.state.age + 1 
    });
  };

  render() {
    return (
      <>
        <button onClick={this.handleAgeChange}>
        Increment age
        </button>
        <p>You are {this.state.age}.</p>
      </>
    );
  }
}

Визначення стану у компонентах класу еквівалентно виклику useState у компонентах функції.

Дивіться, як перенести.


constructor(props)

Конструктор запускається перед тим, як компонент вашого класу змонтується (додасться на екран). Зазвичай, конструктор в React використовується лише для двох цілей. Він дозволяє вам оголошувати стан і прив'язувати методи вашого класу до екземпляру класу:

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { counter: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // ...
  }

Якщо ви використовуєте сучасний синтаксис JavaScript, конструктори рідко потрібні. Замість цього ви можете переписати цей код вище, використовуючи синтаксис полів класу public, який підтримується як сучасними браузерами, так і такими інструментами, як Babel:

class Counter extends Component {
  state = { counter: 0 };

  handleClick = () => {
    // ...
  }

Конструктор не повинен містити побічних ефектів або підписок.

Параметри

  • props: початкові пропси компонента.

Повернення

конструктор не повинен нічого повертати.

Застереження

  • Не запускайте у конструкторі жодних побічних ефектів або підписок. Замість цього використовуйте для цього componentDidMount.

  • Усередині конструктора потрібно викликати super(props) перед будь-яким іншим оператором. Якщо цього не зробити, this.props буде undefined під час роботи конструктора, що може заплутати і спричинити проблеми.

  • Конструктор - це єдине місце, де ви можете призначити this.state безпосередньо. У всіх інших методах ви повинні використовувати this.setState(). Не викликайте setState у конструкторі.

  • Якщо ви використовуєте серверний рендеринг, конструктор буде запущено і на сервері, після чого буде викликано метод render. Однак методи життєвого циклу, такі як componentDidMount або componentWillUnmount не будуть виконуватися на сервері.

  • Коли Суворий режим увімкнено, React викликає конструктор двічі під час розробки, а потім викидає один з екземплярів. Це допоможе вам помітити випадкові побічні ефекти, які потрібно вилучити з конструктора.

Для конструктора у функціональних компонентах не існує точного еквіваленту. Щоб оголосити стан у функціональному компоненті, викличте useState. Щоб уникнути переобчислення початкового стану, передайте функцію до useState.


componentDidCatch(error, info)

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

Зазвичай використовується разом з static getDerivedStateFromError, який дозволяє оновити стан у відповідь на помилку і вивести користувачеві повідомлення про помилку. Компонент з цими методами називається межею помилки.

Дивіться приклад.

Параметри

  • error: Помилка, яку було викинуто. На практиці це зазвичай буде екземпляр Error, але це не гарантовано, оскільки JavaScript дозволяє викидати будь-яке значення, включаючи рядки або навіть null.

  • info: Об'єкт, що містить додаткову інформацію про помилку. Його поле componentStack містить трасування стеку з компонентом, що викинув помилку, а також імена та розташування джерел усіх його батьківських компонентів. У виробництві назви компонентів буде скорочено. Якщо ви налаштуєте виробниче звітування про помилки, ви можете розшифрувати стек компонентів за допомогою мап джерел так само, як це робиться для звичайних стеків помилок JavaScript.

Повернення

componentDidCatch не має нічого повертати.

Застереження

  • У минулому було прийнято викликати setState всередині componentDidCatch, щоб оновити інтерфейс користувача і відобразити повідомлення про помилку. Це застаріло на користь визначення static getDerivedStateFromError.

  • .
  • Виробничі та розробницькі збірки React дещо відрізняються за способом componentDidCatch обробки помилок. У розробці помилки будуть виводитись у вікно window, що означає, що будь-який window.onerror або window.addEventListener('error', callback) перехопить помилки, які були перехоплені componentDidCatch. Натомість, у виробництві помилки не будуть спливати, а це означає, що будь-який обробник помилок предка отримає лише ті помилки, які не були явно перехоплені componentDidCatch.

Прямого еквівалента для componentDidCatch у функціональних компонентах поки що не існує. Якщо ви не хочете створювати компоненти класів, напишіть один компонент ErrorBoundary, як показано вище, і використовуйте його у всьому вашому застосунку. Крім того, ви можете скористатися пакетом react-error-boundary, який зробить це за вас.


componentDidMount()

Якщо ви визначите метод componentDidMount, React викличе його, коли ваш компонент буде додано (змонтовано) на екран. Це звичайне місце для запуску зчитування даних, налаштування підписок або маніпуляцій з вузлами DOM.

Якщо ви реалізуєте componentDidMount, вам зазвичай потрібно реалізувати інші методи життєвого циклу, щоб уникнути помилок. Наприклад, якщо componentDidMount читає якийсь стан або пропси, вам також потрібно реалізувати componentDidUpdate для обробки їх змін, і componentWillUnmount для очищення того, що робив componentDidMount.

class ChatRoom extends Component {
  state = {
    serverUrl: 'https://localhost:1234'
  };

  componentDidMount() {
    this.setupConnection();
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.props.roomId !== prevProps.roomId ||
      this.state.serverUrl !== prevState.serverUrl
    ) {
      this.destroyConnection();
      this.setupConnection();
    }
  }

  componentWillUnmount() {
    this.destroyConnection();
  }

  // ...
}

Дивіться більше прикладів.

Параметри

componentDidMount не приймає жодних параметрів.

Повернення

componentDidMount не має нічого повертати.

Застереження

  • Коли увімкнено Суворий режим, під час розробки React викличе componentDidMount, потім негайно викличе componentWillUnmount, а знову викличе componentDidMount. Це допоможе вам помітити, якщо ви забули реалізувати componentWillUnmount або якщо його логіка не повністю "віддзеркалює" те, що робить componentDidMount.

  • Хоча ви можете викликати setState одразу у componentDidMount, краще уникати цього, якщо це можливо. Це викличе додатковий рендеринг, але він відбудеться до того, як браузер оновить екран. Це гарантує, що навіть якщо render буде викликано двічі, користувач не побачить проміжного стану. Використовуйте цей патерн з обережністю, оскільки він часто спричиняє проблеми з продуктивністю. У більшості випадків, ви зможете призначити початковий стан у конструкторі замість цього. Однак це може знадобитися у таких випадках, як модальні елементи та підказки, коли вам потрібно виміряти вузол DOM перед рендерингом чогось, що залежить від його розміру або положення.

У багатьох випадках визначення componentDidMount, componentDidUpdate та componentWillUnmount разом у компонентах класу еквівалентно виклику useEffect у компонентах функції. У рідкісних випадках, коли важливо, щоб код виконувався до відображення браузера, useLayoutEffect є кращим відповідником.

Дивіться, як мігрувати.


componentDidUpdate(prevProps, prevState, snapshot?)

Якщо ви визначите метод componentDidUpdate, React викличе його одразу після того, як ваш компонент буде повторно рендерити з оновленими пропсами або станом. Цей метод не викликається для початкового рендерингу.

Ви можете використовувати його для маніпулювання DOM після оновлення. Це також звичайне місце для виконання мережевих запитів, якщо ви порівнюєте поточні пропси з попередніми (наприклад, мережевий запит може бути непотрібним, якщо пропси не змінилися). Зазвичай його використовують разом з componentDidMount і componentWillUnmount:

class ChatRoom extends Component {
  state = {
    serverUrl: 'https://localhost:1234'
  };

  componentDidMount() {
    this.setupConnection();
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.props.roomId !== prevProps.roomId ||
      this.state.serverUrl !== prevState.serverUrl
    ) {
      this.destroyConnection();
      this.setupConnection();
    }
  }

  componentWillUnmount() {
    this.destroyConnection();
  }

  // ...
}

Дивіться більше прикладів.

Параметри

  • prevProps: Пропси до оновлення. Порівняйте prevProps з this.props, щоб визначити, що змінилося.

  • prevState: Стан до оновлення. Порівняйте prevState з this.state, щоб визначити, що змінилося.

  • snapshot: Якщо ви застосували getSnapshotBeforeUpdate, snapshot міститиме значення, яке ви повернули з цього методу. Інакше це буде undefined.

Повернення

componentDidUpdate не має нічого повертати.

Застереження

  • componentDidUpdate не буде викликано, якщо визначено shouldComponentUpdate і повертає false.

  • Логіка всередині componentDidUpdate зазвичай повинна бути обгорнута в умови порівняння this.props з prevProps, а this.state з prevState. Інакше існує ризик створення нескінченних циклів.

  • Хоча ви можете викликати setState одразу у componentDidUpdate, краще уникати цього, якщо це можливо. Це викличе додатковий рендеринг, але він відбудеться до того, як браузер оновить екран. Це гарантує, що навіть якщо render буде викликано двічі, користувач не побачить проміжного стану. Цей патерн часто спричиняє проблеми з продуктивністю, але він може бути необхідним у рідкісних випадках, таких як модальні елементи та підказки, коли вам потрібно виміряти вузол DOM перед рендерингом чогось, що залежить від його розміру або положення.

У багатьох випадках визначення componentDidMount, componentDidUpdate та componentWillUnmount разом у компонентах класу еквівалентно виклику useEffect у компонентах функції. У рідкісних випадках, коли важливо, щоб код виконувався до відображення браузера, useLayoutEffect є кращим відповідником.

Дивіться, як мігрувати.


componentWillMount()

Цей API було перейменовано з componentWillMount на UNSAFE_componentWillMount. Стара назва застаріла. У майбутньому старшому релізі React працюватиме лише нова назва.

Запустіть rename-unsafe-lifecycles codemod для автоматичного оновлення компонентів.


componentWillReceiveProps(nextProps)

Цей API було перейменовано з componentWillReceiveProps на UNSAFE_componentWillReceiveProps. Стара назва застаріла. У майбутньому старшому релізі React працюватиме лише нова назва.

Запустіть rename-unsafe-lifecycles codemod для автоматичного оновлення компонентів.


componentWillUpdate(nextProps, nextState)

Цей API було перейменовано з componentWillUpdate на UNSAFE_componentWillUpdate. Стара назва застаріла. У майбутньому старшому релізі React працюватиме лише нова назва.

Запустіть rename-unsafe-lifecycles codemod для автоматичного оновлення компонентів.


componentWillUnmount()

Якщо ви визначите метод componentWillUnmount, React викличе його перед тим, як ваш компонент буде видалено (демонтовано) з екрану. Це звичайне місце для скасування збору даних або видалення підписок.

Логіка всередині componentWillUnmount має "дзеркально" відображати логіку всередині componentDidMount. Наприклад, якщо componentDidMount створює підписку, componentWillUnmount має очистити її. Якщо логіка очищення у вашому componentWillUnmount зчитує якісь пропси або стан, зазвичай вам також потрібно реалізувати componentDidUpdate для очищення ресурсів (наприклад, підписок), що відповідають старим пропсам і стану.

class ChatRoom extends Component {
  state = {
    serverUrl: 'https://localhost:1234'
  };

  componentDidMount() {
    this.setupConnection();
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.props.roomId !== prevProps.roomId ||
      this.state.serverUrl !== prevState.serverUrl
    ) {
      this.destroyConnection();
      this.setupConnection();
    }
  }

  componentWillUnmount() {
    this.destroyConnection();
  }

  // ...
}

Дивіться більше прикладів.

Параметри

componentWillUnmount не приймає жодних параметрів.

Повернення

componentWillUnmount не має нічого повертати.

Застереження

  • Коли увімкнено Суворий режим, під час розробки React викличе componentDidMount,, потім негайно викличе componentWillUnmount, а потім знову викличе componentDidMount. Це допоможе вам помітити, якщо ви забули реалізувати componentWillUnmount або якщо його логіка не повністю "віддзеркалює" те, що робить componentDidMount.

У багатьох випадках визначення componentDidMount, componentDidUpdate та componentWillUnmount разом у компонентах класу еквівалентно виклику useEffect у компонентах функції. У рідкісних випадках, коли важливо, щоб код виконувався до відображення браузера, useLayoutEffect є кращим відповідником.

Дивіться, як мігрувати.


forceUpdate(callback?)

Змушує компонент перерендерити.

Зазвичай це не потрібно. Якщо метод render вашого компонента читає тільки з this.props, this.state або this.context,, він буде перерендерити автоматично при виклику setState всередині вашого компонента або одного з його батьків. Однак, якщо метод render вашого компонента зчитує дані безпосередньо із зовнішнього джерела даних, ви повинні сказати React, щоб він оновив користувацький інтерфейс, коли це джерело даних зміниться. Саме це дозволяє зробити forceUpdate.

Намагайтеся уникати будь-якого використання forceUpdate і лише читати з this.props та this.state у render.

Parameters

  • опціонально зворотний виклик Якщо вказано, React викличе зворотний виклик, який ви надали, після фіксації оновлення.

Повернення

forceUpdate нічого не повертає.

Застереження

  • Якщо ви викликаєте forceUpdate, React повторно відрендерить без виклику shouldComponentUpdate.

Читання зовнішнього джерела даних та примушування компонентів класу перерендерити у відповідь на його зміни за допомогою forceUpdate було замінено на useSyncExternalStore у функціональних компонентах.


getChildContext()

Цей API буде видалено у наступному старшому релізі React. Використовуйте Context.Provider замість нього.

Дозволяє вказати значення для контексту спадковості , який надається цим компонентом.


getSnapshotBeforeUpdate(prevProps, prevState)

Якщо ви застосуєте getSnapshotBeforeUpdate, React викличе його безпосередньо перед оновленням DOM. Це дозволяє вашому компоненту перехопити деяку інформацію з DOM (наприклад, позицію прокрутки) до того, як вона буде змінена. Будь-яке значення, повернуте цим методом життєвого циклу, буде передано як параметр до componentDidUpdate.

Наприклад, ви можете використовувати його у інтерфейсі, наприклад, у потоці чату, який має зберігати позицію прокрутки під час оновлення:

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the scroll position so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // If we have a snapshot value, we've just added new items.
    // Adjust scroll so these new items don't push the old ones out of view.
    // (snapshot here is the value returned from getSnapshotBeforeUpdate)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}

У наведеному прикладі важливо прочитати властивість scrollHeight безпосередньо у getSnapshotBeforeUpdate. Небезпечно читати її в render, UNSAFE_componentWillReceiveProps або UNSAFE_componentWillUpdate, оскільки існує потенційний розрив у часі між викликом цих методів та оновленням React'ом DOM.

Параметри

  • prevProps: Пропси до оновлення. Порівняйте prevProps з this.props, щоб визначити, що змінилося.

  • prevState: Стан до оновлення. Порівняйте prevState з this.state, щоб визначити, що змінилося.

Повернення

Ви маєте повернути значення знімка будь-якого типу, або null. Значення, яке ви повернули, буде передано як третій аргумент до componentDidUpdate.

Застереження

  • getSnapshotBeforeUpdate не буде викликано, якщо визначено shouldComponentUpdate і повертає false.

Наразі не існує еквівалента getSnapshotBeforeUpdate для функціональних компонентів. Цей випадок використання дуже рідкісний, але якщо у вас виникне така потреба, поки що вам доведеться написати компонент класу.


render()

Метод render є єдиним необхідним методом у компоненті класу.

У методі render слід вказати, що ви хочете вивести на екран, наприклад:

import { Component } from 'react';

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

React може викликати render у будь-який момент, тому не варто вважати, що він виконується у певний час. Зазвичай метод render має повертати фрагмент JSX, але підтримується декілька інших типів повернення (наприклад, рядки). Для обчислення повернутого JSX метод render може читати this.props, this.state та this.context.

Метод render слід писати як чисту функцію, тобто він має повертати один і той самий результат, якщо пропси, стан і контекст однакові. Він також не повинен містити побічних ефектів (наприклад, налаштування підписок) або взаємодіяти з API браузера. Побічні ефекти повинні виникати або в обробниках подій, або в методах на кшталт componentDidMount.

.

Параметри

render не приймає жодних параметрів.

Повернення

render може повернути будь-який допустимий вузол React. Це включає React-елементи, такі як <div />, рядки, числа, портали, порожні вузли (null, undefined, true та false) та масиви React-вузлів.

Застереження

  • render слід писати як чисту функцію від пропсів, стану та контексту. Вона не повинна мати побічних ефектів.

  • render не буде викликано, якщо визначено shouldComponentUpdate і повертає false.

  • Коли Суворий режим увімкнено, React викликає рендер двічі під час розробки, а потім викидає один з екземплярів. Це допоможе вам помітити випадкові побічні ефекти, які потрібно вилучити з методу render.

  • Між викликом render та наступним викликом componentDidMount або componentDidUpdate немає однозначної відповідності. Деякі з результатів виклику render можуть бути відхилені React, коли це вигідно.


setState(nextState, callback?)

Викличте setState для оновлення стану вашого React-компонента.

class Form extends Component {
  state = {
    name: 'Taylor',
  };

  handleNameChange = (e) => {
    const newName = e.target.value;
    this.setState({
      name: newName
    });
  }

  render() {
    return (
      <>
        <input value={this.state.name} onChange={this.handleNameChange} />
        <p>Hello, {this.state.name}.</p>
      </>
    );
  }
}

setState виводить зміни стану компонента. Це повідомляє React, що цей компонент та його дочірні компоненти потрібно перерендерити з новим станом. Це основний спосіб оновлення інтерфейсу користувача у відповідь на взаємодію.

Виклик setState не змінює поточний стан у вже виконуваному коді:

function handleClick() {
  console.log(this.state.name); // "Taylor"
  this.setState({
    name: 'Robin'
  });
  console.log(this.state.name); // Still "Taylor"!
}

Впливає лише на те, що this.state повертатиме, починаючи з наступного рендеру.

Ви також можете передати функцію до setState. Вона дозволяє оновлювати стан на основі попереднього стану:

handleIncreaseAge = () => {
    this.setState(prevState => {
      return {
        age: prevState.age + 1
      };
    });
  }

Цього робити не обов'язково, але це зручно, якщо ви хочете оновити стан кілька разів під час однієї події.

Параметри

  • nextState: Або об'єкт, або функція.

    • Якщо ви передасте об'єкт як nextState, він буде неглибоко об'єднаний у this.state.
    • Якщо ви передасте функцію як nextState, вона буде розглядатися як функція оновлення. Вона має бути чистою, приймати стан очікування та пропси як аргументи і повертати об'єкт, який буде неглибоко злитий у this.state. React поставить вашу функцію оновлення в чергу і повторно відрендерить ваш компонент. Під час наступного рендерингу React обчислить наступний стан, застосувавши всі оновлювачі з черги до попереднього стану.
  • опціонально зворотний виклик: якщо вказано, React викличе зворотний виклик, який ви надали, після фіксації оновлення.

Повернення

setState нічого не повертає.

Застереження

  • Сприймайте setState як запит, а не як безпосередню команду для оновлення компонента. Коли декілька компонентів оновлюють свій стан у відповідь на подію, React об'єднає їх оновлення в пакет і відрендерить їх разом за один прохід в кінці події. У рідкісних випадках, коли вам потрібно примусово застосувати певне оновлення стану синхронно, ви можете обернути його в flushSync,, але це може погіршити продуктивність.

  • setState не оновлює this.state негайно. Це робить читання this.state одразу після виклику setState потенційною пасткою. Замість цього використовуйте componentDidUpdate або аргумент setState callback, будь-який з яких гарантовано спрацює після застосування оновлення. Якщо вам потрібно встановити стан на основі попереднього стану, ви можете передати функцію до nextState, як описано вище.

Виклик setState у компонентах класу подібний до виклику функції set у компонентах функцій.

Дивіться, як перенести.


shouldComponentUpdate(nextProps, nextState, nextContext)

Якщо ви визначите shouldComponentUpdate, React викличе його, щоб визначити, чи можна пропустити повторний рендеринг.

Якщо ви впевнені, що хочете написати його вручну, ви можете порівняти this.props з nextProps і this.state з nextState і повернути false, щоб повідомити React, що оновлення може бути пропущено.

class Rectangle extends Component {
  state = {
    isHovered: false
  };

  shouldComponentUpdate(nextProps, nextState) {
    if (
      nextProps.position.x === this.props.position.x &&
      nextProps.position.y === this.props.position.y &&
      nextProps.size.width === this.props.size.width &&
      nextProps.size.height === this.props.size.height &&
      nextState.isHovered === this.state.isHovered
    ) {
      // Nothing has changed, so a re-render is unnecessary
      return false;
    }
    return true;
  }

  // ...
}

Викликати shouldComponentUpdate перед рендерингом при отриманні нових пропсів або стану. За замовчуванням true. Цей метод не викликається для початкового рендерингу або коли використовується forceUpdate.

Параметри

  • nextProps: Наступні пропси, з якими компонент буде рендерити. Порівняйте nextProps з this.props, щоб визначити, що змінилося.
  • nextState: наступний стан, з яким компонент буде рендеритися. Порівняйте nextState з this.state, щоб визначити, що змінилося.
  • nextContext: Наступний контекст, з яким компонент буде рендеритись. Порівняйте nextContext з this.context, щоб визначити, що змінилося. Доступно лише якщо вказати static contextType (сучасний) або static contextTypes (застарілий).

Повернення

Поверніть true, якщо ви хочете, щоб компонент повторно відрендерився. Це поведінка за замовчуванням.

Повернути false, щоб повідомити React, що повторний рендеринг можна пропустити.

Застереження

  • Цей метод існує лише як оптимізація продуктивності. Якщо ваш компонент ламається без нього, спочатку виправте це.

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

  • Ми не рекомендуємо робити глибокі перевірки на рівність або використовувати JSON.stringify в shouldComponentUpdate. Це робить продуктивність непередбачуваною і залежною від структури даних кожного реквізиту і стану. У кращому випадку ви ризикуєте спричинити багатосекундні зависання програми, а у гіршому - призвести до її аварійного завершення.

  • Повернення false не заважає дочірнім компонентам повторно рендеритися при зміні їхнього стану.

  • Повернення false не гарантує, що компонент не буде повторно рендеритись. React використовуватиме значення, що повертається, як підказку, але все одно може вирішити повторно відрендерити ваш компонент, якщо це має сенс з інших причин.

Оптимізація компонентів класів за допомогою shouldComponentUpdate подібна до оптимізації функціональних компонентів за допомогою memo. Функціональні компоненти також пропонують більш детальну оптимізацію за допомогою useMemo.


UNSAFE_componentWillMount()

Якщо ви визначите UNSAFE_componentWillMount, React викличе його одразу після конструктора. Він існує лише з історичних причин і не повинен використовуватися в новому коді. Натомість, використовуйте одну з альтернатив:

  • Щоб ініціалізувати стан, оголосіть state як поле класу або встановіть this.state всередині конструктора.
  • Якщо вам потрібно запустити побічний ефект або налаштувати підписку, перенесіть цю логіку до componentDidMount замість цього.

Дивіться приклади переходу від небезпечних життєвих циклів.

Параметри

UNSAFE_componentWillMount не приймає жодних параметрів.

Повернення

UNSAFE_componentWillMount не має нічого повертати.

Застереження

  • UNSAFE_componentWillMount не буде викликано, якщо компонент реалізує static getDerivedStateFromProps або getSnapshotBeforeUpdate.

  • Незважаючи на назву, UNSAFE_componentWillMount не гарантує, що компонент буде змонтовано, якщо ваш застосунок використовує сучасні можливості React, такі як Suspense. Якщо спробу рендерингу призупинено (наприклад, через те, що код якогось дочірнього компонента ще не завантажився), React відкине незавершене дерево і спробує побудувати компонент з нуля під час наступної спроби. Ось чому цей метод є "небезпечним". Код, який покладається на монтування (наприклад, додавання підписки), має бути розміщений у componentDidMount.

  • UNSAFE_componentWillMount єдиний метод життєвого циклу, який виконується під час рендерингу сервера. Для всіх практичних цілей він є ідентичним конструктору, тому вам слід використовувати конструктор для цього типу логіки замість нього.

Виклик setState всередині UNSAFE_componentWillMount у компоненті класу для ініціалізації стану еквівалентний передачі цього стану як початкового стану до useState у компоненті функції.


UNSAFE_componentWillReceiveProps(nextProps, nextContext)

Якщо ви визначите UNSAFE_componentWillReceiveProps, React буде викликати його, коли компонент отримає нові пропси. Він існує лише з історичних причин і не повинен використовуватися в новому коді. Натомість, використовуйте одну з альтернатив:

  • Якщо вам потрібно запустити побічний ефект (наприклад, отримати дані, запустити анімацію або переініціалізувати підписку) у відповідь на зміну пропсів, перенесіть цю логіку до componentDidUpdate натомість.
  • Якщо вам потрібно уникнути повторного обчислення деяких даних лише при зміні пропсів, використовуйте замість цього помічник запам'ятовування.
  • Якщо вам потрібно "скинути" деякий стан при зміні реквізиту, розгляньте можливість зробити компонент повністю керованим або повністю некерованим за допомогою ключа замість цього.
  • Якщо вам потрібно "підлаштувати" деякий стан при зміні пропсів,перевірте, чи можете ви обчислити всю необхідну інформацію лише з пропсів під час рендерингу. Якщо не можете, використовуйте натомість static getDerivedStateFromProps.

Дивіться приклади міграції з небезпечних життєвих циклів.

Параметри

  • nextProps: Наступні пропси, які компонент отримає від батьківського компонента. Порівняйте nextProps з this.props, щоб визначити, що змінилося.
  • nextContext: Наступний контекст, який компонент отримає від найближчого постачальника. Порівняйте nextContext з this.context, щоб визначити, що змінилося. Доступно лише якщо вказати static contextType (сучасний) або static contextTypes (застарілий).

Повертає

UNSAFE_componentWillReceiveProps не має нічого повертати.

Застереження

  • UNSAFE_componentWillReceiveProps не буде викликано, якщо компонент реалізує static getDerivedStateFromProps або getSnapshotBeforeUpdate.

  • Незважаючи на назву, UNSAFE_componentWillReceiveProps не гарантує, що компонент отримає ці пропси, якщо ваш застосунок використовує сучасні можливості React, такі як Suspense. Якщо спробу рендерингу призупинено (наприклад, через те, що код якогось дочірнього компонента ще не завантажився), React відкине незавершене дерево і спробує побудувати компонент з нуля під час наступної спроби. На момент наступної спроби рендерингу пропси можуть змінитися. Ось чому цей метод є "небезпечним". Код, який має виконуватися лише для фіксованих оновлень (наприклад, скидання підписки), слід розмістити у componentDidUpdate.

  • .
  • UNSAFE_componentWillReceiveProps не означає, що компонент отримав інші пропси, ніж минулого разу. Вам потрібно самостійно порівняти nextProps та this.props, щоб перевірити, чи щось змінилося.

  • React не викликає UNSAFE_componentWillReceiveProps з початковими пропсами під час монтування. Він викликає цей метод лише тоді, коли потрібно оновити деякі пропси компонента. Наприклад, виклик setState зазвичай не викликає UNSAFE_componentWillReceiveProps всередині того самого компонента.

Виклик setState всередині UNSAFE_componentWillReceiveProps у компоненті класу для "коригування" стану еквівалентний виклику функції set з useState під час рендерингу у компоненті функції.


UNSAFE_componentWillUpdate(nextProps, nextState)

Якщо ви визначите UNSAFE_componentWillUpdate, React викличе його перед рендерингом з новими пропсами або станом. Він існує лише з історичних причин і не повинен використовуватися в новому коді. Натомість, використовуйте одну з альтернатив:

  • Якщо вам потрібно запустити побічний ефект (наприклад, отримати дані, запустити анімацію або повторно ініціалізувати підписку) у відповідь на зміну реквізиту або стану, перенесіть цю логіку до componentDidUpdate натомість.
  • Якщо вам потрібно прочитати деяку інформацію з DOM (наприклад, зберегти поточну позицію прокрутки), щоб потім використати її у componentDidUpdate, прочитайте її всередині getSnapshotBeforeUpdate замість цього.

Дивіться приклади переходу від небезпечних життєвих циклів.

Параметри

  • nextProps: Наступні пропси, з якими компонент буде рендерити. Порівняйте nextProps з this.props, щоб визначити, що змінилося.
  • nextState: наступний стан, з яким компонент буде рендеритися. Порівняйте nextState з this.state, щоб визначити, що змінилося.

Повернення

UNSAFE_componentWillUpdate не має нічого повертати.

Застереження

  • UNSAFE_componentWillUpdate не буде викликано, якщо визначено shouldComponentUpdate і повертає false.

  • UNSAFE_componentWillUpdate не буде викликано, якщо компонент реалізує static getDerivedStateFromProps або getSnapshotBeforeUpdate.

  • Не підтримується виклик setState (або будь-який метод, який призводить до виклику setState, наприклад, диспетчеризація дії Redux) під час componentWillUpdate.

  • Незважаючи на назву, UNSAFE_componentWillUpdate не гарантує, що компонент буде оновлюватися, якщо ваш застосунок використовує сучасні можливості React, такі як Suspense. Якщо спробу рендерингу призупинено (наприклад, через те, що код якогось дочірнього компонента ще не завантажився), React відкине незавершене дерево і спробує побудувати компонент з нуля під час наступної спроби. На момент наступної спроби рендерингу пропси та стан можуть змінитися. Ось чому цей метод є "небезпечним". Код, який має виконуватися лише для фіксованих оновлень (наприклад, скидання підписки), слід розмістити у componentDidUpdate.

  • .
  • UNSAFE_componentWillUpdate не означає, що компонент отримав інші пропси або стан, ніж минулого разу. Вам потрібно самостійно порівняти nextProps з this.props та nextState з this.state, щоб перевірити, чи щось змінилося.

  • React не викликає UNSAFE_componentWillUpdate з початковими пропсами та станом під час монтування.

Не існує прямого еквівалента UNSAFE_componentWillUpdate у функціональних компонентах.


static childContextTypes

Цей API буде видалено в наступному старшому релізі React. Використовуйте static contextType замість нього.

Дозволяє вказати, який контекст спадковості використовується цим компонентом.


static contextTypes

Цей API буде видалено у наступному старшому релізі React. Використовуйте static contextType замість нього.

Дозволяє вказати, який контекст спадковості використовується цим компонентом.


static contextType

Якщо ви хочете прочитати this.context з компонента вашого класу, ви маєте вказати, який контекст йому потрібно прочитати. Контекст, який ви вкажете як static contextType, має бути значенням, раніше створеним за допомогою createContext.

class Button extends Component {
  static contextType = ThemeContext;

  render() {
    const theme = this.context;
    const className = 'button-' + theme;
    return (
      <button className={className}>
        {this.props.children}
      </button>
    );
  }
}

Читання this.context у компонентах класу еквівалентно useContext у компонентах функції.

Дивіться, як мігрувати.


static defaultProps

Ви можете визначити static defaultProps для встановлення пропсів за замовчуванням для класу. Їх буде використано для невизначених та відсутніх пропсів, але не для нульових пропсів.

Наприклад, ось як можна визначити, що проп color за замовчуванням повинен мати значення 'blue':

class Button extends Component {
  static defaultProps = {
    color: 'blue'
  };

  render() {
    return <button className={this.props.color}>click me</button>;
  }
}

Якщо проп color не вказано або undefined, за замовчуванням буде встановлено значення 'blue':

<>
  {/* this.props.color is "blue" */}
  <Button />

  {/* this.props.color is "blue" */}
  <Button color={undefined} />

  {/* this.props.color is null */}
  <Button color={null} />

  {/* this.props.color is "red" */}
  <Button color="red" />
</>

Визначення defaultProps у компонентах класів подібне до використання значень за замовчуванням у компонентах функцій.


static propTypes

Ви можете визначити static propTypes разом з бібліотекою prop-types, щоб оголосити типи пропсів, які приймає ваш компонент. Ці типи буде перевірено лише під час рендерингу та розробки.

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  static propTypes = {
    name: PropTypes.string
  };

  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Рекомендується використовувати TypeScript замість перевірки типів пропсів під час виконання.


static getDerivedStateFromError(error)

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

Зазвичай використовується разом з componentDidCatch, який дозволяє відправити звіт про помилку до якогось аналітичного сервісу. Компонент з цими методами називається межею помилки.

Дивіться приклад.

Параметри

  • error: Помилка, яку було викинуто. На практиці це зазвичай буде екземпляр Error, але це не гарантовано, оскільки JavaScript дозволяє викидати будь-яке значення, включаючи рядки або навіть null.

Повернення

static getDerivedStateFromError має повернути стан, який вказує компоненту на необхідність виведення повідомлення про помилку.

Застереження

  • static getDerivedStateFromError має бути чистою функцією. Якщо ви хочете виконати побічний ефект (наприклад, викликати службу аналітики), вам потрібно також реалізувати componentDidCatch.

Прямого еквівалента для static getDerivedStateFromError у функціональних компонентах поки що не існує. Якщо ви не хочете створювати компоненти класів, напишіть один компонент ErrorBoundary, як показано вище, і використовуйте його у всьому вашому застосунку. Або скористайтеся пакетом react-error-boundary, який робить це саме так.


static getDerivedStateFromProps(props, state)

Якщо ви визначите static getDerivedStateFromProps, React викличе його безпосередньо перед викликом render, як при початковому монтуванні, так і при наступних оновленнях. Вона має повернути об'єкт для оновлення стану або null, щоб нічого не оновлювати.

Цей метод існує для рідкісних випадків використання, коли стан залежить від зміни реквізиту з часом. Наприклад, цей Form компонент скидає стан пошти при зміні userIDпропсу:

class Form extends Component {
  state = {
    email: this.props.defaultEmail,
    prevUserID: this.props.userID
  };

  static getDerivedStateFromProps(props, state) {
    // Any time the current user changes,
    // Reset any parts of state that are tied to that user.
    // In this simple example, that's just the email.
    if (props.userID !== state.prevUserID) {
      return {
        prevUserID: props.userID,
        email: props.defaultEmail
      };
    }
    return null;
  }

  // ...
}

Зверніть увагу, що цей патерн вимагає збереження попереднього значення пропу (як userID) у стані (як prevUserID).

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

Параметри

  • props: наступні пропси, з якими компонент буде рендерити.
  • state: наступний стан, з яким компонент збирається рендерити.

Повернення

static getDerivedStateFromProps повернути об'єкт для оновлення стану, або null щоб нічого не оновлювати.

Застереження

  • Цей метод викликається при кожному рендерингу, незалежно від причини. Це відрізняється від UNSAFE_componentWillReceiveProps, який спрацьовує лише тоді, коли батько викликає повторний рендеринг, а не в результаті локального setState.

  • Цей метод не має доступу до екземпляра компонента. Якщо бажаєте, ви можете повторно використати деякий код між static getDerivedStateFromProps та іншими методами класу, витягнувши чисті функції пропсів та стану компонента поза визначенням класу.

Реалізація static getDerivedStateFromProps у компоненті класу еквівалентна виклику функції set з useState під час рендерингу у компоненті функції.


Використання

Визначення компонента класу

Щоб визначити React-компонент як клас, розширьте вбудований клас Component та визначте метод render:

import { Component } from 'react';

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

React буде викликати ваш метод render щоразу, коли йому потрібно буде визначити, що виводити на екран. Зазвичай, ви повернете з нього деякий JSX. Ваш метод render має бути чистою функцією: він має обчислювати лише JSX.

Подібно до функціональних компонентів, компонент класу може отримувати інформацію за допомогою пропсів від батьківського компонента. Однак синтаксис читання пропсів відрізняється. Наприклад, якщо батьківський компонент рендерить <Greeting name="Taylor" />, то ви можете прочитати проп name з this.props, як this.props.name:

import { Component } from 'react';

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default function App() {
  return (
    <>
      <Greeting name="Sara" />
      <Greeting name="Cahal" />
      <Greeting name="Edite" />
    </>
  );
}

Зверніть увагу, що хуки (функції, що починаються з use, як useState) не підтримуються всередині компонентів класу.

Рекомендується визначати компоненти як функції замість класів. Дивіться, як мігрувати.


Додавання стану до компонента класу

Щоб додати state до класу, призначте об'єкт властивості з назвою state. Щоб оновити стан, викличте this.setState.

import { Component } from 'react';

export default class Counter extends Component {
  state = {
    name: 'Taylor',
    age: 42,
  };

  handleNameChange = (e) => {
    this.setState({
      name: e.target.value
    });
  }

  handleAgeChange = () => {
    this.setState({
      age: this.state.age + 1 
    });
  };

  render() {
    return (
      <>
        <input
          value={this.state.name}
          onChange={this.handleNameChange}
        />
        <button onClick={this.handleAgeChange}>
          Increment age
        </button>
        <p>Hello, {this.state.name}. You are {this.state.age}.</p>
      </>
    );
  }
}
button { display: block; margin-top: 10px; }

Рекомендується визначати компоненти як функції замість класів. Дивіться, як мігрувати.


Додавання методів життєвого циклу до компонента класу

Існує декілька спеціальних методів, які ви можете визначити у своєму класі.

Якщо ви визначите метод componentDidMount, React викличе його, коли ваш компонент буде додано (змонтовано) на екран. React викличе componentDidUpdate після того, як ваш компонент повторно відрендериться через зміну пропсів або стану. React викличе componentWillUnmount після того, як ваш компонент буде видалено (демонтовано) з екрану.

Якщо ви реалізуєте componentDidMount, вам зазвичай потрібно реалізувати всі три життєві цикли, щоб уникнути помилок. Наприклад, якщо componentDidMount читає якийсь стан або пропси, вам також потрібно реалізувати componentDidUpdate для обробки їх змін і componentWillUnmount для очищення того, що робив componentDidMount.

Наприклад, цей ChatRoom компонент синхронізує з'єднання чату з пропсами та станом:

import { useState } from 'react';
import ChatRoom from './ChatRoom.js';

export default function App() {
  const [roomId, setRoomId] = useState('general');
  const [show, setShow] = useState(false);
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <button onClick={() => setShow(!show)}>
        {show ? 'Close chat' : 'Open chat'}
      </button>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId} />}
    </>
  );
}
import { Component } from 'react';
import { createConnection } from './chat.js';

export default class ChatRoom extends Component {
  state = {
    serverUrl: 'https://localhost:1234'
  };

  componentDidMount() {
    this.setupConnection();
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.props.roomId !== prevProps.roomId ||
      this.state.serverUrl !== prevState.serverUrl
    ) {
      this.destroyConnection();
      this.setupConnection();
    }
  }

  componentWillUnmount() {
    this.destroyConnection();
  }

  setupConnection() {
    this.connection = createConnection(
      this.state.serverUrl,
      this.props.roomId
    );
    this.connection.connect();    
  }

  destroyConnection() {
    this.connection.disconnect();
    this.connection = null;
  }

  render() {
    return (
      <>
        <label>
          Server URL:{' '}
          <input
            value={this.state.serverUrl}
            onChange={e => {
              this.setState({
                serverUrl: e.target.value
              });
            }}
          />
        </label>
        <h1>Welcome to the {this.props.roomId} room!</h1>
      </>
    );
  }
}
export function createConnection(serverUrl, roomId) {
  // A real implementation would actually connect to the server
  return {
    connect() {
      console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...');
    },
    disconnect() {
      console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl);
    }
  };
}
input { display: block; margin-bottom: 20px; }
button { margin-left: 10px; }

Зверніть увагу, що під час розробки, коли увімкнено Суворий режим, React викличе componentDidMount, негайно викличе componentWillUnmount, а потім знову викличе componentDidMount. Це допоможе вам помітити, якщо ви забули реалізувати componentWillUnmount або якщо його логіка не повністю "віддзеркалює" те, що робить componentDidMount.

Ми рекомендуємо визначати компоненти як функції замість класів. Дивіться, як мігрувати.


Перехоплення помилок рендерингу з межею помилки

За замовчуванням, якщо ваш застосунок видасть помилку під час рендерингу, React прибере його інтерфейс з екрану. Щоб запобігти цьому, ви можете обернути частину вашого інтерфейсу в межу помилки. Межа помилки - це спеціальний компонент, який дозволяє відображати деякий запасний інтерфейс замість частини, яка зазнала аварійного завершення, наприклад, повідомлення про помилку.

Щоб реалізувати компонент межі помилки, вам потрібно надати static getDerivedStateFromError, який дозволяє оновлювати стан у відповідь на помилку і показувати користувачеві повідомлення про помилку. Ви також можете додатково реалізувати componentDidCatch для додавання додаткової логіки, наприклад, для реєстрації помилки у службі аналітики.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // Example "componentStack":
    //   in ComponentThatThrows (created by App)
    //   in ErrorBoundary (created by App)
    //   in div (created by App)
    //   in App
    logErrorToMyService(error, info.componentStack);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return this.props.fallback;
    }

    return this.props.children;
  }
}

Після цього ви можете обгорнути ним частину дерева компонентів:

<ErrorBoundary fallback={<p>Something went wrong</p>}>
  <Profile />
</ErrorBoundary>

Якщо Profile або його дочірній компонент згенерує помилку, ErrorBoundary "перехопить" цю помилку, відобразить резервний інтерфейс із повідомленням про помилку, яке ви надали, і надішле звіт про виробничу помилку до вашої служби звітування про помилки.

Вам не потрібно обгортати кожен компонент в окрему межу помилок. Коли ви думаєте про деталізацію меж помилок, подумайте, де є сенс показувати повідомлення про помилку. Наприклад, у програмі для обміну повідомленнями має сенс розмістити межу помилки навколо списку розмов. Також має сенс розмістити її навколо кожного окремого повідомлення. Однак не має сенсу розміщувати межу навколо кожного аватара.

Наразі не існує способу записати межу помилки як компонент функції. Втім, вам не обов'язково писати клас межі помилки самостійно. Наприклад, ви можете використати react-error-boundary замість

.

Альтернативи

Переміщення простого компонента з класу у функцію

Зазвичай ви визначатимете компоненти як функції замість цього.

Наприклад, припустимо, що ви перетворюєте цей компонент класу Greeting на функцію:

import { Component } from 'react';

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default function App() {
  return (
    <>
      <Greeting name="Sara" />
      <Greeting name="Cahal" />
      <Greeting name="Edite" />
    </>
  );
}

Визначте функцію з назвою Greeting. Це місце, куди ви будете переміщувати тіло вашої функції рендерингу .

function Greeting() {
  // ... move the code from the render method here ...
}

Замість this.props.name визначте проп name за допомогою синтаксису деструктуризації і читайте його безпосередньо:

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

Ось повний приклад:

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

export default function App() {
  return (
    <>
      <Greeting name="Sara" />
      <Greeting name="Cahal" />
      <Greeting name="Edite" />
    </>
  );
}

Переміщення компонента зі станом з класу у функцію

Припустимо, ви перетворюєте компонент класу Counter на функцію:

import { Component } from 'react';

export default class Counter extends Component {
  state = {
    name: 'Taylor',
    age: 42,
  };

  handleNameChange = (e) => {
    this.setState({
      name: e.target.value
    });
  }

  handleAgeChange = (e) => {
    this.setState({
      age: this.state.age + 1 
    });
  };

  render() {
    return (
      <>
        <input
          value={this.state.name}
          onChange={this.handleNameChange}
        />
        <button onClick={this.handleAgeChange}>
          Increment age
        </button>
        <p>Hello, {this.state.name}. You are {this.state.age}.</p>
      </>
    );
  }
}
button { display: block; margin-top: 10px; }

Почніть з оголошення функції з необхідними змінними стану:

import { useState } from 'react';

function Counter() {
  const [name, setName] = useState('Taylor');
  const [age, setAge] = useState(42);
  // ...

Далі, перетворіть обробники подій:

function Counter() {
  const [name, setName] = useState('Taylor');
  const [age, setAge] = useState(42);

  function handleNameChange(e) {
    setName(e.target.value);
  }

  function handleAgeChange() {
    setAge(age + 1);
  }
  // ...

Нарешті, замініть усі посилання, починаючи з цього на змінні та функції, які ви визначили у вашому компоненті. Наприклад, замініть this.state.age на age, а this.handleNameChange на handleNameChange.

Ось повністю перетворений компонент:

import { useState } from 'react';

export default function Counter() {
  const [name, setName] = useState('Taylor');
  const [age, setAge] = useState(42);

  function handleNameChange(e) {
    setName(e.target.value);
  }

  function handleAgeChange() {
    setAge(age + 1);
  }

  return (
    <>
      <input
        value={name}
        onChange={handleNameChange}
      />
      <button onClick={handleAgeChange}>
        Increment age
      </button>
      <p>Hello, {name}. You are {age}.</p>
    </>
  )
}
button { display: block; margin-top: 10px; }

Перенесення компонента з методами життєвого циклу з класу у функцію

Припустимо, ви перетворюєте цей компонент класу ChatRoom з методами життєвого циклу на функцію:

import { useState } from 'react';
import ChatRoom from './ChatRoom.js';

export default function App() {
  const [roomId, setRoomId] = useState('general');
  const [show, setShow] = useState(false);
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <button onClick={() => setShow(!show)}>
        {show ? 'Close chat' : 'Open chat'}
      </button>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId} />}
    </>
  );
}
import { Component } from 'react';
import { createConnection } from './chat.js';

export default class ChatRoom extends Component {
  state = {
    serverUrl: 'https://localhost:1234'
  };

  componentDidMount() {
    this.setupConnection();
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.props.roomId !== prevProps.roomId ||
      this.state.serverUrl !== prevState.serverUrl
    ) {
      this.destroyConnection();
      this.setupConnection();
    }
  }

  componentWillUnmount() {
    this.destroyConnection();
  }

  setupConnection() {
    this.connection = createConnection(
      this.state.serverUrl,
      this.props.roomId
    );
    this.connection.connect();    
  }

  destroyConnection() {
    this.connection.disconnect();
    this.connection = null;
  }

  render() {
    return (
      <>
        <label>
          Server URL:{' '}
          <input
            value={this.state.serverUrl}
            onChange={e => {
              this.setState({
                serverUrl: e.target.value
              });
            }}
          />
        </label>
        <h1>Welcome to the {this.props.roomId} room!</h1>
      </>
    );
  }
}
export function createConnection(serverUrl, roomId) {
  // A real implementation would actually connect to the server
  return {
    connect() {
      console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...');
    },
    disconnect() {
      console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl);
    }
  };
}
input { display: block; margin-bottom: 20px; }
button { margin-left: 10px; }

Перш за все, переконайтеся, що ваш componentWillUnmount робить протилежне до componentDidMount. У наведеному прикладі це так: він розриває з'єднання, яке встановлює componentDidMount. Якщо така логіка відсутня, спочатку додайте її.

Далі, переконайтеся, що ваш метод componentDidUpdate обробляє зміни будь-яких пропсів та стану, які ви використовуєте у componentDidMount. У наведеному вище прикладі componentDidMount викликає setupConnection, який читає this.state.serverUrl і this.props.roomId. Ось чому componentDidUpdate перевіряє, чи змінилися this.state.serverUrl і this.props.roomId, і скидає з'єднання, якщо вони змінилися. Якщо ваша логіка componentDidUpdate відсутня або не обробляє зміни всіх відповідних пропсів та станів, спочатку виправте це.

У наведеному вище прикладі логіка всередині методів життєвого циклу підключає компонент до системи поза React (чат-серверу). Щоб підключити компонент до зовнішньої системи, опишіть цю логіку як один ефект:

import { useState, useEffect } from 'react';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);

  // ...
}

Цей виклик useEffect еквівалентний логіці у методах життєвого циклу вище. Якщо ваші методи життєвого циклу виконують кілька непов'язаних дій, розділіть їх на кілька незалежних ефектів. Ось повний приклад, з яким ви можете погратися:

import { useState } from 'react';
import ChatRoom from './ChatRoom.js';

export default function App() {
  const [roomId, setRoomId] = useState('general');
  const [show, setShow] = useState(false);
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <button onClick={() => setShow(!show)}>
        {show ? 'Close chat' : 'Open chat'}
      </button>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId} />}
    </>
  );
}
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [roomId, serverUrl]);

  return (
    <>
      <label>
        Server URL:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}
export function createConnection(serverUrl, roomId) {
  // A real implementation would actually connect to the server
  return {
    connect() {
      console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...');
    },
    disconnect() {
      console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl);
    }
  };
}
input { display: block; margin-bottom: 20px; }
button { margin-left: 10px; }

Якщо ваш компонент не синхронізується із зовнішніми системами, можливо, вам не потрібен ефект.


Переміщення компонента з контекстом з класу у функцію

У цьому прикладі компоненти класів Panel та Button читають контекст з this.context:

import { createContext, Component } from 'react';

const ThemeContext = createContext(null);

class Panel extends Component {
  static contextType = ThemeContext;

  render() {
    const theme = this.context;
    const className = 'panel-' + theme;
    return (
      <section className={className}>
        <h1>{this.props.title}</h1>
        {this.props.children}
      </section>
    );    
  }
}

class Button extends Component {
  static contextType = ThemeContext;

  render() {
    const theme = this.context;
    const className = 'button-' + theme;
    return (
      <button className={className}>
        {this.props.children}
      </button>
    );
  }
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}
.panel-light,
.panel-dark {
  border: 1px solid black;
  border-radius: 4px;
  padding: 20px;
}
.panel-light {
  color: #222;
  background: #fff;
}

.panel-dark {
  color: #fff;
  background: rgb(23, 32, 42);
}

.button-light,
.button-dark {
  border: 1px solid #777;
  padding: 5px;
  margin-right: 10px;
  margin-top: 10px;
}

.button-dark {
  background: #222;
  color: #fff;
}

.button-light {
  background: #fff;
  color: #222;
}

При перетворенні їх у функціональні компоненти замініть this.context на виклики useContext:

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}
.panel-light,
.panel-dark {
  border: 1px solid black;
  border-radius: 4px;
  padding: 20px;
}
.panel-light {
  color: #222;
  background: #fff;
}

.panel-dark {
  color: #fff;
  background: rgb(23, 32, 42);
}

.button-light,
.button-dark {
  border: 1px solid #777;
  padding: 5px;
  margin-right: 10px;
  margin-top: 10px;
}

.button-dark {
  background: #222;
  color: #fff;
}

.button-light {
  background: #fff;
  color: #222;
}