Сохранение состояния в React Component: особенности работы метода setState

Сохранение состояния в React Component: особенности работы метода setState


04.09.2018
/
oberset

Прежде всего давайте разберемся, что такое внутреннее состояние компонента в React и для чего оно может понадобиться. Состояние компонента – это некие данные, которые сохраняются внутри компонента и влияют на его поведение. К примеру, мы хотим создать компонент, который добавляет к форме элемент управления checkbox. В этом случае нам понадобиться где то хранить текущее значение checked=true/false. Как раз для таких целей лучше всего подходит внутренний state компонента.
import React, {Component} from 'react';

class Checkbox extends Component {
constructor(props) {
super(props);
this.state = {
checked: false
}
}
handleChange = (event) => {
this.setState(({checked}) => ({
checked: !checked
}));
};
render() {
const {checked} = this.state;
return (
<input
type="checkbox"
checked={checked}
onChange={this.handleChange}
/>
);
}
}

Компоненты React могут сохранять свое собственное состояние в объекте state, который представляет собой обычный Object, хранящий пары ключ-значение. Единственное место, где можно задавать значения свойств this.state напрямую – это конструктор компонента (если вы используете babel-plugin-transform-class-properties, то можно определить state как свойство в теле класса). В примере выше мы создали в конструкторе объект state со свойством checked и установили начальное значение в false. Далее, в методе render мы используем это значение для отображения текущего состояния элемента. После того, как мы кликнем по checkbox, сработает событие change и новое значение свойства checked запишется в state.

Как вы уже заметили, для записи нового значения свойства объекта state мы вызвали метод setState. Как уже было сказано, модифицировать объект state напрямую можно только в конструкторе, в остальных случаях нужно вызывать метод setState.

Какие параметры принимает setState?

Самый простой способ обновить состояние компонента — это передать в метод setState объект с новыми значениями, которые нужно записать в state:
setState({
checked: true,
value: 1
});

Если нужно вычислить новое значение на основе текущего, то вместо объекта в метод setState можно передать функцию:
setState((currentState) => ({
checked: !currentState.checked,
value: 1
}));

Функция в качестве параметра получает текущий объект state, значения которого можно использовать для формирования новых значений. Этот вариант вызова setState более предпочтительный.

Вторым параметром setState принимает функцию, которая вызовется после обновления состояния:

setState((state) => ({
checked: !state.checked
}), () => {
console.log('Component updated!');
});

Этот параметр не является обязательным и редко используется, вместо него лучше использовать метод componentDidUpdate.

Вызов setState асинхронный

Какое значение выведется в консоль после вызова setState?
// this.state = { value: 0 };
setState({value: 1});
console.log(this.state.value); // 0

На первый взгляд это может показаться странным, но значение value, которое выводится в консоль не изменилось после вызова setState. Причина в том, что setState работает асинхронно и не изменяет state в текущем digist-цикле.

Какое значение в итоге запишется в state?
// this.state = { value: 0 };
setState({value: this.state.value + 1});
setState({value: this.state.value + 1});
setState({value: this.state.value + 1});
console.log(this.state.value); // 0

Рассмотрев предыдущий пример, нетрудно догадаться, что после всех вызовов setState в this.state попадет {value: 1}. React объединяет объекты, переданные в каждом вызове setState. Значение this.state.value в текущем digist-цикле не изменяется, поэтому мы получим value = 0 + 1 во всех 3 вызовах. В консоль при этом по-прежнему выведется значение 0.
componentDidUpdate() {
console.log(this.state.value); // 1
}

Все меняется, если передавать в setState функцию вместо объекта:
// this.state = { value: 0 };
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));

В этом случае this.state.value будет равным 3, как и ожидалось. Если новое значение в state вычисляется на основе текущего состояния, всегда передавайте в setState функцию, а не объект. Почему для обновления состояния лучше передавать функцию? Когда React запустит цикл обновления компонента, он будет последовательно выполнять каждую функцию, которую мы передали в setState, сохраняя в state возвращаемое ей значение.

Обновление состояния в асинхронном коде:
componentDidMount() {
setTimeout(() => {
this.setState({value: this.state.value + 1});
this.setState({value: this.state.value + 1});
this.setState({value: this.state.value + 1});
console.log(this.state.value); // 3
});
}

Внутри setTimeout метод setState ведет себя по-другому. Этот код выведет в консоль 3, а не 0, как было в примере выше. Нужно учитывать эту особенность при работе с асинхронным кодом.



Как в React повесить DOM-событие на window или document?

Иногда внутри React-компонента требуется повесить DOM-событие на window или document, например, если нужно добавить обработчик события resize или scroll окна браузера.

Композиция с помощью HOC (Higher Order Component) в React

Компонент высшего порядка в React (Higher Order Component или сокращенно HOC) – это простая javascript-функция, которая принимает на вход один или несколько компонентов и возвращает другой компонент.

Разбираемся со связкой React и Redux на простейшем примере

Простой пример, который поможет быстро разобраться, как управлять данными с помощью библиотеки Redux в ваших приложениях на React.