SkillGridИзучай computer science на практике

State: данные, которые меняются, пока страница открыта

Почему экрану нужно сообщить об изменении, чем props отличаются от state и почему прямая запись в state заставляет обновление исчезнуть.

14 мин · 45 XP

Кнопка «нравится» — одна из самых маленьких программ, которыми люди реально пользуются. Она хранит число, прибавляет единицу по щелчку и показывает результат. Почти все сложности фронтенда прячутся внутри этих трёх шагов.

Начнём с наивной версии, той, которую первой пишет каждый новичок.

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} />;Шаг задаётся снаружи, счёт принадлежит компоненту.
UserLikeButtonScreenclicksetLikes(likes + 1)the function runs againnew elementsnobody reloaded the page

Практика

  1. Компонент счётчика решает, сколько прибавлять за щелчок, по значению step, а текущую сумму хранит сам. Что из этих двух является state?

    Подсказки
    • Число на экране берётся из одного из двух, и только одно из них этот компонент может записывать.
    • Prop — это значение, переданное снаружи. Изменить его внутри значило бы, что компонент переписывает собственный вход.
  2. Сопоставьте каждое значение с тем, приходит ли оно как prop или принадлежит как state.

    Подсказки
    • Спросите, кому позволено записывать каждое значение: самому компоненту или тому, кто ставит компонент на страницу.
    • Два из них принадлежат компоненту и меняются, пока он живёт; остальные два задаются снаружи.
  3. По кнопке «нравится» щёлкнули. Расставьте то, что происходит дальше, по порядку.

    1. 1Надпись на кнопке показывает новое число
    2. 2Функция компонента выполняется заново и читает новое значение
    3. 3Вызывается установщик с новым числом
    4. 4Срабатывает обработчик щелчка
    5. 5React запоминает новое значение для этого компонента
    Подсказки
    • Нельзя нарисовать то, что ещё не установлено, поэтому вызов установщика идёт раньше новой картинки.
    • Компонент выполняется заново после изменения значения, поэтому тело функции — не первый шаг следующего прохода.