React.js російською мовою. Частина п'ята



Переклад офіційної документації бібліотеки React.js російською мовою.

Зміст:
1 — Частина перша
2 — Частина друга
3 — Частина третя
4 — Частина четверта
5 — Частина п'ята
6 — Частина шоста (скоро)

Стан і життєвий цикл
На даний момент, ми знаємо тільки один спосіб як оновити інтерфейс.
Ми відправляємо сигнал ReactDOM.render() щоб змінити виводяться дані:

function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}

setInterval(tick, 1000);

Спробуйте повторити цей приклад CodePen.

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

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

unction Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}

function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}

setInterval(tick, 1000);

Спробуйте повторити цей приклад CodePen.

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

В ідеалі, ми хочемо прописати код годин один раз і отримати такі годинники, які будуть самообновляться:

ReactDOM.render(
<Clock />,
document.getElementById('root')
);

Щоб реалізувати задумане, нам потрібно додати до компоненту Clock стан. Стан це теж саме, що і властивості, але воно є закритим і повністю контролюється компонентом.

Ми згадували раніше, що компоненти визначаються як класи з деякими додатковими характеристиками. Локальне стан передбачає функцію, доступну тільки для класів.

Перетворення функції в клас
У п'ять кроків ви можете перетворити такий функціональний компонент, як Clock в клас:

  1. Створіть клас ES6 з тією ж назвою, яка успадковує React.Component
  2. Додайте до цього кроку один порожній алгоритм з назвою render()
  3. Перенесіть тіло функції в алгоритм render()
  4. Замініть props this.props в тілі render()
  5. Видаліть залишилися порожні опису функції
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

Спробуйте повторити цей приклад CodePen.

Clock тепер визначається як клас, а не функція. А тепер давайте спробуємо додаткові характеристики: локальне стан і прив'язки життєвого циклу.

Додавання локального остояния до класу

Тепер ми перемістимо date властивості в стан, слідуючи трьох дій:

1. Замініть this.props.date на this.state.date в алгоритм render()

class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

2. Додайте class constructor, яким присвоюються ініціали this.state

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

Зверніть увагу, як ми передаємо props в базовий конструктор:

constructor(props) {
super(props);
this.state = {date: new Date()};
}

Компоненти класу завжди повинні віддавати сигнал в конструктор props:

3. Приберіть властивість date елемента <Clock />

ReactDOM.render(
<Clock />,
document.getElementById('root')
);

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

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

ReactDOM.render(
<Clock />,
document.getElementById('root')
);

Спробуйте повторити цей приклад CodePen. Далі, ми зробимо так, щоб Clock встановив свій таймер і самообновлялся кожну секунду.

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

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

Ми хочемо встановити таймер, коли Clock виводяться в DOM вперше.

Ми також хочемо, щоб таймер очищався кожен раз, коли DOM, вироблений від Clock, видаляється.

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

componentDidMount() {

}

componentWillUnmount() {

}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

Ці алгоритми називаються прив'язками життєвого циклу.

Прив'язка componentDidMount() виконуватися після того, як результат виконання компонента виводиться DOM. Тут якраз підходяще місце для таймера:

componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}

Зверніть увагу, що ми зберігаємо ID таймера this.

У той час як React встановлює this.props, this.state має особливе значення, ви можете вручну додати додаткові поля до класу, якщо ви хочете зберігати те, що не використовується для візуального виводу.

Якщо ви нічого не використовуєте в render(), то не потрібно нічого додавати.

Ми розберемо таймер в прив'язці життєвого циклу componentWillUnmount():

componentWillUnmount() {
clearInterval(this.timerID);
}

Нарешті, ми впровадимо алгоритм tick(), який виконується кожну секунду. Він буде використовувати this.setState() з схематичними оновленнями локального стану компонента:

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}

componentWillUnmount() {
clearInterval(this.timerID);
}

tick() {
this.setState({
date: new Date()
});
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

ReactDOM.render(
<Clock />,
document.getElementById('root')
);

Спробуйте повторити цей приклад CodePen. Тепер годинник цокає кожну секунду.

Правильне використання стану

Є три речі, які стосуються setState(), про які ви повинні знати.

Не змінюйте стан напряму

Наприклад, так перемалювати компонент не вийде:

// Wrong
this.state.comment = 'Hello';

Замість цього, використовуйте setState():

// Correct
this.setState({comment: 'Hello'});

Оновлення стану можуть бути асинхронними.

React може упакувати численні сигнали setState() в одне ціле оновлення для обчислення продуктивності.

Оскільки this.props this.state можуть оновлюватися асинхронно, не варто спиратися на їх сукупні значення для обчислення наступного стану.

Наприклад, цей код може не оновити лічильник:

// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});

Щоб виправити це, використовуйте другу форму setState(), яка приймає функцію, а не об'єкт. Ця функція доставить попередній стан як перший аргумент, і під час оновлення властивості буде виступати як другий аргумент:

// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));

Вище ми використовували стрелочную функцію (arrow function), але вона також застосовується до регулярних функцій:

// Correct
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});

Оновлення стану об'єднані

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

constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}

Потім ви можете відновити їх незалежно, окремими setState():

componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});

fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}

Злиття відбувається неглибоко, тому this.setState({comments}) залишає this.state.posts недоторканими, але повністю замінює this.state.comments.

Потік інформації

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

Саме тому стан часто називається локальним або инкапсулированным. Воно недоступне жодному компоненту, крім того, який встановлює стан і управляє ним.

Компонент здатний передавати свій стан як властивість для своїх дочірніх компонентів.

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

Цей приклад також підходить для користувальницьких компонентів:

<FormattedDate date={this.state.date} />

Компонент FormattedDate буде отримувати date в свої властивості і не буде знати чи є дані отримані від стану Clock, або вони були введені вручну:

function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

Спробуйте повторити цей приклад CodePen.

Зазвичай, такий потік інформації називається низхідним або односпрямований. Будь-який стан, яка управляється певним компонентом і будь-яка інформація або UI, вироблена з цього стану, може вплинути тільки на «нижні» компоненти дерева.

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

Щоб показати, що всі елементи дійсно ізольовані, ми можемо створити компонент програми (App), який відображає три компоненти <Clock>:

function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}

ReactDOM.render(
<App />,
document.getElementById('root')
);

Спробуйте повторити цей приклад CodePen.

Кожен Clock встановлює свої таймер і незалежно оновлюється.

У додатках React, незалежно від того, чи є компонент відслідковувати чи ні, він є деталлю реалізації компоненту, який з часом може змінюватися. Ви можете використовувати невідстежувані компоненти всередині відслідковуються і навпаки.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.