Компонент
Ми рекомендуємо визначати компоненти як функції замість класів. Дивіться, як мігрувати.
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
або аргумент setStatecallback
, будь-який з яких гарантовано спрацює після застосування оновлення. Якщо вам потрібно встановити стан на основі попереднього стану, ви можете передати функцію до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
).
Отримання стану призводить до багатослівного коду та ускладнює продумування компонентів. Переконайтеся, що ви знайомі з простішими альтернативами:
- Якщо вам потрібно виконати побічний ефект (наприклад, отримання даних або анімацію) у відповідь на зміну пропсів, використовуйте метод
componentDidUpdate
натомість. - Якщо ви хочете переобчислювати деякі дані лише при зміні пропсів, використовуйте замість цього помічник запам'ятовування.
- Якщо ви хочете "скинути" деякий стан при зміні пропса, розгляньте можливість зробити компонент повністю керованим або повністю некерованим за допомогою ключа натомість.
Параметри
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;
}