State: данные, которые меняются, пока страница открыта
Почему экрану нужно сообщить об изменении, чем props отличаются от state и почему прямая запись в state заставляет обновление исчезнуть.
Кнопка «нравится» — одна из самых маленьких программ, которыми люди реально пользуются. Она хранит число, прибавляет единицу по щелчку и показывает результат. Почти все сложности фронтенда прячутся внутри этих трёх шагов.
Начнём с наивной версии, той, которую первой пишет каждый новичок.
let likes = 0;
function like() {
likes = likes + 1;
}
like();
like();React решает это, выдавая значение и способ его изменить одним движением. useState возвращает пару: значение сейчас и функцию-установщик. Вызовите установщик, и компонент выполнится заново с новым значением.
import { useState } from "react";
function LikeButton() {
const [likes, setLikes] = useState(0);
return (
<button onClick={() => setLikes(likes + 1)}>
{likes} likes
</button>
);
}Значение и способ его изменить, возвращённые вместе.У двух половин этой строки разные задачи. likes — это то, что видит и рисует текущий запуск компонента. setLikes — единственный честный способ изменить то, что увидит следующий запуск. Кнопка, надпись на которой никогда не меняется, почти всегда оказывается кнопкой, которая меняет переменную и не вызывает установщик.
Та же идея работает и с вводом текста. Поле поиска, которое теряет напечатанное, — классический симптом поля, которое никто не запоминает.
function SearchBox() {
const [text, setText] = useState("");
return (
<form>
<input value={text} onChange={(e) => setText(e.target.value)} />
<p>You typed: {text}</p>
</form>
);
}Поле показывает то, что хранит state, а нажатие клавиши записывает state обратно.А теперь ловушка, в которую попадают все, обычно со списком дел. Массивы и объекты — не значения в том смысле, в каком числа: это ссылки на место в памяти. Изменение содержимого оставляет ссылку ровно там, где она была.
const [items, setItems] = useState(["Bread", "Tea"]);
function addBad(name) {
items.push(name);
setItems(items);
}
function addGood(name) {
setItems([...items, name]);
}Одна из этих двух версий оставляет список на экране без изменений.Итак, в компоненте есть два вида данных, и разделяет их один вопрос: кому позволено их менять? Props приходят снаружи и доступны только для чтения. State принадлежит компоненту и меняется. Счётчику с настраиваемым шагом нужно и то и другое.
function Counter({ step }) {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + step)}>
{count}
</button>
);
}
const a = <Counter step={1} />;
const b = <Counter step={5} />;Шаг задаётся снаружи, счёт принадлежит компоненту.Практика
Компонент счётчика решает, сколько прибавлять за щелчок, по значению step, а текущую сумму хранит сам. Что из этих двух является state?
Подсказки
- Число на экране берётся из одного из двух, и только одно из них этот компонент может записывать.
- Prop — это значение, переданное снаружи. Изменить его внутри значило бы, что компонент переписывает собственный вход.
Сопоставьте каждое значение с тем, приходит ли оно как prop или принадлежит как state.
Подсказки
- Спросите, кому позволено записывать каждое значение: самому компоненту или тому, кто ставит компонент на страницу.
- Два из них принадлежат компоненту и меняются, пока он живёт; остальные два задаются снаружи.
По кнопке «нравится» щёлкнули. Расставьте то, что происходит дальше, по порядку.
- 1Надпись на кнопке показывает новое число
- 2Функция компонента выполняется заново и читает новое значение
- 3Вызывается установщик с новым числом
- 4Срабатывает обработчик щелчка
- 5React запоминает новое значение для этого компонента
Подсказки
- Нельзя нарисовать то, что ещё не установлено, поэтому вызов установщика идёт раньше новой картинки.
- Компонент выполняется заново после изменения значения, поэтому тело функции — не первый шаг следующего прохода.