3.12.3 Хук состояния
Хуки доступны в версии React 16.8. Они позволяют вам использовать состояние и другие функции React без написания класса.
В предыдущем разделе хуки были представлены с помощью данного примера:
import { 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 />;
}
Ранее вы, возможно, знали их как «компоненты без состояния». Сейчас эти компоненты получают возможность использовать состояние, поэтому для них мы предпочитаем название «компоненты-функции».
Хуки не работают внутри классов, однако теперь вы можете их использовать вместо написания самих классов.
Наш новый пример начинается с импорта хука useState
из React:
import { useState } from 'react';
function Example() {
// ...
}
Что такое хук? Хук - это специальная функция, которая позволяет «прицепиться»
к возможностям React. Например, useState
- это хук, который позволяет добавлять
состояние React к компонентам-функциям. С другими хуками мы познакомимся позже.
Когда можно использовать хук? Если вы пишете компонент-функцию и в какой-то понимаете, что вам нужно добавить в него какое-то состояние, то ранее вам приходилось преобразовывать его в класс. Теперь вы можете использовать хук внутри существующего компонента-функции. Давайте сделаем это прямо сейчас!
Внимание!
Есть некоторые специальные правила относительно того, где вы можете и не можете использовать хуки в компоненте. Мы узнаем о них в разделе Правила использования хуков.
В классе мы инициализируем состояние счетчика значением 0
,
устанавливая this.state
равным {count: 0}
в конструкторе:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
В компоненте-функции у нас такой возможности нет, поэтому мы не
можем назначить или прочитать this.state
. Вместо этого
мы вызываем хук useState
прямо внутри нашего компонента:
import { useState } from 'react';
function Example() {
// Объявляем новую переменную состояния, кокторую назовём "count"
const [count, setCount] = useState(0);
Что делает вызов useState
? Он объявляет «переменную состояния». Это способ
«сохранять» некоторые значения между вызовами функций. Наша переменная называется count
,
но мы можем назвать ее как угодно, например, banana
. useState
- это новый способ
использовать точно такие же возможности, которые this.state
предоставляет в классе.
Обычно переменные теряются при выходе из функции, но переменные состояния сохраняются React-ом.
Что мы передаем useState
в качестве аргумента? Единственный аргумент для хука useState()
-
это начальное состояние. В отличие от классов, состояние не обязательно должно быть объектом.
Мы можем сохранять число или строку, если это все, что нам нужно. В нашем примере мы хотим хранить
просто число, показывающее сколько раз пользователь кликал, поэтому передаем 0
в качестве начального
состояния для нашей переменной. (Если бы мы хотели сохранить два разных значения в состоянии,
мы бы вызвали useState()
дважды.)
Что возвращает useState
? Он возвращает два значения: текущее состояние и функцию, которая
его обновляет. Вот почему мы пишем const [count, setCount] = useState()
. Это похоже на this.state.count
и
this.setState
в классе, за исключением того, что вы получаете их в паре. Если вы не знакомы с синтаксисом,
который мы использовали, мы вернемся к нему внизу этого раздела.
Теперь, когда мы знаем, что делает хук useState
, наш пример должен иметь больше смысла:
import { useState } from 'react';
function Example() {
// Объявляем новую переменную состояния, кокторую назовём "count"
const [count, setCount] = useState(0);
Мы объявляем переменную состояния с именем count
и устанавливаем ее равной 0
.
React запоминает ее текущее значение между повторными отрисовками и предоставляет самое
последнее значение для нашей функции. Если мы хотим обновить текущее значение счетчика,
мы можем вызвать setCount
.
Внимание!
Вы можете задаться вопросом: почемуuseState
не называется createState
?
Слово «создать» будет не совсем точным, поскольку состояние создается только при первой отрисовке нашего компонента. Во время следующих отрисовок
useState
возвращает нам текущее состояние. Иначе это не является "состоянием" вообще! Существует также
причина, по которой имена хуков всегда начинаются с use
.
Мы узнаем почему в разделе Правила использования хуков.
Когда мы хотим отобразить текущий счетчик в классе, мы
считываем this.state.count
:
<p>Вы кликнули {this.state.count} раз</p>
В функции мы можем использовать count
напрямую:
<p>Вы кликнули {count} раз</p>
В классе нам нужно вызвать this.setState()
, чтобы
обновить состояние счетчика:
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Кликни меня
</button>
В функции у нас теперь есть переменные setCount
и count
,
поэтому такой вызов не нужен:
<button onClick={() => setCount(count + 1)}>
Кликни меня
</button>
Теперь давайте резюмируем то, что мы узнали, построчно и проверим наше понимание.
1: import { 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 повторно выполнит отрисовку компонентаExample
, передав ему новое значениеcount
.
Поначалу это может показаться слишком сложным. Но не торопитесь! Если вы запутались в нашем объяснении, снова посмотрите на приведенный выше код и попробуйте прочитать его сверху вниз. Мы обещаем, что как только вы попытаетесь «забыть», как работает состояние в классах, и посмотрите на этот код свежим взглядом, его смысл станет полностью понятен.
3.12.3.7.1 Подсказка: что означают квадратные скобки?
Вы могли заметить квадратные скобки, когда мы объявляем переменную состояния:
const [count, setCount] = useState(0);
Имена слева не являются частью React API. Вы можете назначить свои собственные имена переменным состояния:
const [fruit, setFruit] = useState('банан');
Данный синтаксис JavaScript называется «деструктуризация массива». Он означает, что мы
создаем две новые переменные fruit
и setFruit
, где во fruit
устанавливается первое значение,
возвращаемое useState
, а в setFruit
второе. Это эквивалентно следующему коду:
var fruitStateVariable = useState('банан'); // возвращает пару
var fruit = fruitStateVariable[0]; // первый элемент в паре
var setFruit = fruitStateVariable[1]; // второй элемент в паре
Когда мы объявляем переменную состояния с помощью useState
, он возвращает пару - массив с
двумя элементами. Первый элемент - это текущее значение, а второй - функция, которая
позволяет нам его обновлять. Использование [0]
и [1]
для доступа к ним немного сбивает с толку,
поскольку они имеют конкретное смысловое значение. Вот почему вместо этого мы используем
деструктуризацию массива.
Внимание!
Вам может быть любопытно, как React узнает, какому компоненту соответствуетuseState
, ведь мы не передаем React никакой
информации вроде this
. Мы ответим на этот и многие другие
вопросы в разделе FAQ по хукам.
3.12.3.7.1 Подсказка: использование множества переменных состояния
Объявление переменных состояния как пары [нечто, setНечто]
также удобно
потому, что позволяет нам давать им разные имена, если мы хотим использовать несколько переменных:
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. Он позволяет выполнять побочные эффекты в компонентах и аналогичен методам жизненного цикла в классах.