Состояние компонента React (State)
Компоненты могут иметь состояние (state), которое является приватной частью данных компонента и может использоваться для управления динамическими данными.
Состояние применяется только к классовым компонентам, или его можно использовать в функциональных компонентах при использовании React Hooks.
React рассматривает компоненты как конечные автоматы. Через взаимодействие с пользователями реализуются разные состояния, а затем рендерится UI, чтобы поддерживать соответствие пользовательского интерфейса данным.
В React вам нужно только обновить состояние компонента, а затем повторно отрендерить пользовательский интерфейс на основе нового состояния (не манипулируйте DOM).
В следующем примере создается класс ES6, имя которого расширяет React.Component, и используется this.state в методе render() для изменения текущего времени.
Добавьте конструктор класса для инициализации состояния this.state. Классовые компоненты всегда должны вызывать базовый конструктор с props.
Управление состоянием в классовых компонентах
Создайте классовый компонент с состоянием:
Файл Counter.js
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<p>Счётчик: {this.state.count}</p>
<button onClick={this.increment}>Увеличить</button>
</div>
);
}
}
export default Counter;
Отрендерите компонент в src/index.js:
Пример
import ReactDOM from 'react-dom';
import './index.css';
import Counter from './Counter';
const root = ReactDOM.createRoot(document.getElementById("root"));
// Отрисовать компонент Counter
root.render(<Counter />);
Управление состоянием в функциональных компонентах (с использованием хуков)
Используя хуки React, вы можете использовать состояние в функциональных компонентах. Наиболее часто используемый хук — useState.
Создайте функциональный компонент с состоянием:
Файл Counter.js
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Количество: {count}</p>
<button onClick={() => setCount(count + 1)}>Увеличить</button>
</div>
);
}
export default Counter;
Отрендерите компонент в src/index.js:
Пример
import ReactDOM from 'react-dom';
import './index.css';
import Counter from './Counter';
const root = ReactDOM.createRoot(document.getElementById("root"));
// Отрисовать компонент Counter
root.render(<Counter />);
Пример
Создайте пример Clock, чтобы понять состояние компонента:
Пример React
Попробуйте »
Далее мы заставим Clock настроить собственный таймер и обновляться каждую секунду.
Добавление методов жизненного цикла в класс
В приложениях с множеством компонентов очень важно освобождать ресурсы, занятые компонентами, при их уничтожении.
Когда компонент Clock впервые загружается в DOM, мы хотим настроить таймер. В React это называетсямонтирование。
Аналогично, когда DOM, сгенерированный Clock, удаляется, мы также хотим очистить таймер. В React это называетсяразмонтирование。
Мы можем объявить специальные методы в классе компонента, чтобы выполнить некоторый код, когда компонент монтируется или размонтируется:
Пример React
Попробуйте »
Анализ примера:
componentDidMount()иcomponentWillUnmount()Эти методы называются хуками жизненного цикла.
После того как вывод компонента отрендерен в DOM, выполняетсяcomponentDidMount()хук, и мы можем установить таймер в этом хуке.
this.timerID — это ID таймера, и мы можемcomponentWillUnmount()очистить таймер в этом хуке.
Порядок выполнения кода:
-
Когда
<Clock />передаётся вReactDOM.render(), React вызываетClockконструктор компонента. ПосколькуClockдолжен отображать текущее время, он инициализируется объектом, содержащим текущее времяthis.state. Мы обновим это состояние позже. -
Затем React вызывает
Clockу компонентаrender()метод. Так React узнаёт, что должно отображаться на экране, а затем React обновляет DOM, чтобы привести его в соответствиеClockрезультат рендеринга -
Когда
ClockКогда вывод вставляется в DOM, React вызываетcomponentDidMount()хук жизненного цикла. В нём,Clockкомпонент просит браузер установить таймер, срабатывающий раз в секундуtick()。 -
Браузер вызывает
tick()метод каждую секунду. В нём,Clockкомпонент вызываетsetState()с объектом, содержащим текущее время, чтобы запланировать обновление UI. ВызываяsetState(), React знает, что состояние изменилось, и вызываетrender()снова, чтобы определить, что должно отображаться на экране. На этот раз,render()в методе,this.state.dateбудет другим, поэтому результат рендеринга будет содержать обновлённое время, и DOM обновляется соответствующим образом. -
Как только
Clockкомпонент удаляется из DOM, React вызываетcomponentWillUnmount()эту функцию хука, и таймер будет очищен.
Данные передаются сверху вниз
Ни родительские, ни дочерние компоненты не могут знать, является ли компонент с состоянием или без состояния, и их не должно волновать, определён ли компонент как функция или как класс.
Вот почему состояние часто называют локальным или инкапсулированным. Никакие другие компоненты не могут получить к нему доступ, кроме компонента, который владеет им и устанавливает его.
В следующем примере компонент FormattedDate получает значение date в своих props и не знает, приходит ли оно из состояния Clock, из props Clock или из ручного ввода:
Пример React
Попробуйте »
Это часто называют нисходящим или односторонним потоком данных. Любое состояние всегда принадлежит какому-то конкретному компоненту, и любые данные или UI, производные от этого состояния, могут влиять только на компоненты ниже него в дереве.
Если представить дерево компонентов как водопад props, то состояние каждого компонента — как дополнительный источник воды, который присоединяется в произвольной точке, но также течёт вниз.
Чтобы показать, что все компоненты действительно изолированы, мы можем создать компонент App, который рендерит три компонента Clock:
Пример React
Попробуйте »
В приведённом выше примере каждый компонент Clock настраивает свой собственный таймер и обновляется независимо.
В приложениях React то, является ли компонент с состоянием или без состояния, считается деталью реализации компонента, которая может со временем меняться.
Мы можем использовать компоненты без состояния внутри компонентов с состоянием, а также использовать компоненты с состоянием внутри компонентов без состояния.
Другие расширения