Компонент
Ми рекомендуємо визначати компоненти як функції замість класів. Дивіться, як мігрувати.
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;
}