Использование хука состояния
Хуки — нововведение в React 16.8, которое позволяет использовать состояние и другие возможности React без написания классов.
На странице введения в хуки мы познакомились с ними на этом примере:
import React, { useState } from 'react';
function Example() {
// Объявление новой переменной состояния «count» const [count, setCount] = useState(0);
return (
<div>
<p>Вы кликнули {count} раз(а)</p>
<button onClick={() => setCount(count + 1)}>
Нажми на меня
</button>
</div>
);
}
Давайте начнём изучать хуки, сравнив этот код с эквивалентным кодом на основе класса.
Эквивалентный пример с классом
Если вы уже пользовались классами в React, то вам знаком такой код:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>Вы кликнули {this.state.count} раз(а)</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Нажми на меня
</button>
</div>
);
}
}
Сначала состояние выглядит как { count: 0 }
. Каждый раз, когда пользователь кликает, мы увеличиваем state.count
на единицу, вызывая this.setState()
. Мы будем использовать фрагменты этого класса на протяжении всей страницы.
Примечание
Возможно, вы спросите себя, почему мы используем в качестве примера счётчик, а не что-то более реалистичное. Дело в том, что мы хотим обратить ваше внимание на API, одновременно делая первые шаги с хуками.
Хуки и функциональные компоненты
Напоминаем, что функциональные компоненты в React выглядят так:
const Example = (props) => {
// Тут мог бы быть ваш хук!
return <div />;
}
или так:
function Example(props) {
// Тут мог бы быть ваш хук!
return <div />;
}
Возможно, вы слышали, что такие компоненты называются «компонентами без состояния». Сейчас мы покажем, как использовать внутри них состояние React, поэтому будем называть их «функциональными компонентами».
Хуки НЕ работают внутри классов, а используются вместо них.
Что такое хук?
Наш новый пример начинается с того, что импортирует хук useState
из React:
import React, { useState } from 'react';
function Example() {
// ...
}
Что такое хук? Хук — это специальная функция, которая позволяет «подцепиться» к возможностям React. Например, хук useState
предоставляет функциональным компонентам доступ к состоянию React. Мы узнаем про другие хуки чуть позже.
Когда применить хук? Раньше, если вы писали функциональный компонент и осознавали, что вам нужно наделить его состоянием, вам приходилось превращать этот компонент в класс. Теперь же вы можете использовать хук внутри существующего функционального компонента. Мы покажем это прямо сейчас!
Примечание:
Есть специальные правила о том, где можно, а где нельзя использовать хуки внутри компонента. Их мы изучим в главе Правила хуков.
Объявление переменной состояния
Допустим, мы хотим инициализировать в классе состояние count
значением 0
. Для этого в его конструкторе присваиваем this.state
объект { count: 0 }
:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 }; }
В функциональном компоненте нам недоступен this
, поэтому мы не можем задать или считать состояние через this.state
. Вместо этого мы вызываем хук useState
напрямую изнутри нашего компонента.
import React, { useState } from 'react';
function Example() {
// Объявление новой переменной состояния «count» const [count, setCount] = useState(0);
Что делает вызов useState
? Он объявляет «переменную состояния». Мы называли переменную count
, но могли дать ей любое имя, хоть банан
. Таким образом мы можем «сохранить» некоторые значения между вызовами функции. useState
это новый способ использовать те же возможности, что даёт this.state
в классах. Обычно переменные «исчезают» при выходе из функции. К переменным состояния это не относится, потому что их сохраняет React.
Какие аргументы передавать useState
? Единственный аргумент useState
это исходное состояние. В отличие от случая с классами, состояние может быть и не объектом, а строкой или числом, если нам так удобно. Поскольку в нашем примере отслеживается количество сделанных пользователем кликов, мы передаём 0
в качестве исходного значения переменной. (Если нам нужно было бы хранить два разных значения в состоянии, то пришлось бы вызвать useState()
дважды.)
Что возвращается из useState
? Вызов useState
вернёт пару значений: текущее состояние и функцию, обновляющую состояние. Поэтому мы пишем const [count, setCount] = useState()
. Это похоже на this.state.count
и this.setState
в классах, с той лишь разницей, что сейчас мы принимаем их сразу в паре. Если вам незнаком использованный синтаксис, мы вернёмся к нему ближе к концу страницы.
Теперь мы знаем, что делает useState
, и пример должен быть ясен:
import React, { useState } from 'react';
function Example() {
// Объявление новой переменной состояния «count» const [count, setCount] = useState(0);
Мы объявляем переменную состояния count
и устанавливаем ей значение 0
. React будет помнить текущее (наиболее свежее) значение между рендерингами и передавать его нашей функции. Если мы захотим изменить count
, мы вызовем setCount
.
Примечание
Может быть, вы спросите себя, почему
useState
не назвалиcreateState
?Слово «create» («создать») было бы не совсем точно, потому что состояние создаётся только в момент, когда компонент рендерится впервые. В последующие же рендеринги
useState
возвращает текущее состояние. Иначе не существовало бы «состояния» как такового. Названия всех хуков начинаются с «use» тоже неспроста. О причине мы узнаем из Правил хуков.
Чтение состояния
Когда мы хотим отобразить текущее состояние счётчика в классе, мы обращаемся к this.state.count
:
<p>Вы кликнули {this.state.count} раз(а)</p>
В функции же мы можем использовать count
напрямую:
<p>Вы кликнули {count} раз(а)</p>
Обновление состояния
В классе мы вызываем this.setState()
, когда надо обновить состояние count
:
<button onClick={() => this.setState({ count: this.state.count + 1 })}> Нажми на меня
</button>
В функции нам не нужен this
, потому что setCount
и count
уже доступны как переменные:
<button onClick={() => setCount(count + 1)}> Нажми на меня
</button>
Резюме
Давайте построчно пробежимся по тому, что мы выучили и проверим наши знания:
1: import React, { useState } from 'react'; 2:
3: function Example() {
4: const [count, setCount] = useState(0); 5:
6: return (
7: <div>
8: <p>Вы кликнули {count} раз(а)</p>
9: <button onClick={() => setCount(count + 1)}>10: Нажми на меня
11: </button>
12: </div>
13: );
14: }
- Строка 1: Импортируем хук
useState
из React. Он позволяет функциональному компоненту хранить внутреннее состояние. - Строка 4: Объявляем внутри компонента
Example
новую переменную состояния, вызвав хукuseState
. Этот вызов возвращает пару значений, которым мы даём имена. Поскольку наша переменная состояния хранит количество сделанных по кнопке кликов, мы называем еёcount
. Чтобы проинициализировать её, мы передаём значение0
в качестве единственного аргумента функцииuseState
. Второе возвращённое нам значение позволяет обновлятьcount
, поэтому мы называем еёsetCount
. - Строка 9: Когда пользователь кликает по кнопке, мы вызываем
setCount
с приращённым значением. После этого React сделает повторный рендер, в котором использует уже новое значениеcount
.
Поначалу это всё может показаться слишком сложным. Не торопитесь! Если вы запутались в объяснении, ещё раз прочитайте приведённый код с начала до конца. Обещаем, если вы на минутку «забудете», как состояние работает в классах, и посмотрите на код свежим взглядом, всё станет ясно.
Совет: Что делают квадратные скобки?
Вы могли обратить внимание на квадратные скобки в месте, где объявляется переменная состояния:
const [count, setCount] = useState(0);
Два имени в квадратных скобках не содержатся в API React. Названия переменным состояния выбираете вы:
const [fruit, setFruit] = useState('банан');
Такой синтаксис в JavaScript называется
«деструктуризацией массивов (array destructuring)». Он означает, что мы создаём две новые переменные, fruit
и setFruit
. Во fruit
будет записано первое значение, вернувшееся из useState
, а в setFruit
— второе, что равносильно такому коду:
var fruitStateVariable = useState('банан'); // Возвращает пару значений
var fruit = fruitStateVariable[0]; // Извлекаем первое значение
var setFruit = fruitStateVariable[1]; // Извлекаем второе значение
Когда мы объявляем переменную состояния с помощью функции useState
, мы получаем от неё пару, то есть массив из двух элементов. Первый элемент обозначает текущее значение, а второй является функцией, позволяющей менять это значение. Доступ к элементам через [0]
и [1]
менее ясен, потому что индексы лишены осмысленных имён.
Примечание
Вам может быть любопытно, а как же React знает, какому компоненту соответствует какой вызов
useState
, если мы не передаём React ниthis
ни чего-либо подобного. Ответ на этот и многие другие вопросы мы дадим в FAQ.
Совет: Использование нескольких переменных состояния
Объявлять переменные состояния через пару [something, setSomething]
удобно ещё и тем, что когда нам нужны несколько переменных, мы можем назвать каждую из них собственным именем:
function ExampleWithManyStates() {
// Объявим несколько переменных состояния!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('банан');
const [todos, setTodos] = useState([{ text: 'Изучить хуки' }]);
В примере выше мы видим локальные переменные age
, fruit
и todos
, которые можем обновлять независимо друг от друга:
function handleOrangeClick() {
// Аналогично коду this.setState({ fruit: 'апельсин' })
setFruit('апельсин');
}
Использовать несколько переменных состояния совсем не обязательно, потому что они могут быть объектами или массивами, которые группируют связанные по смыслу данные. Обратите внимание, что, в отличие от this.setState
в классах, обновление переменной состояния всегда замещает её значение, а не осуществляет слияние.
Подробные рекомендации о разделении независимых переменных состояния вы найдёте в FAQ.
Следующие шаги
На этой странице мы изучили хук React под названием useState
. Иногда мы будем ссылаться на него как на «хук состояния». Он позволяет добавить состояние в функциональные компоненты React, в чём мы убедились на примере!
Мы узнали ещё немного больше о хуках — функциях, позволяющих функциональным компонентам «подцепиться» к возможностям React. Их имена всегда начинаются с use
. Существует много других хуков, которые мы пока не рассматривали.
А теперь давайте перейдём к изучению хука useEffect
, похожего на методы жизненного цикла в классах. С его помощью компоненты могут выполнять побочные эффекты.