PassFront
  1. PassFront
  2. База знаний
  3. Хуки

Как в useState сохранить первоначальное состояние чтобы компонент не перерендовался?

Хуки

Скорее всего, речь о двух вещах сразу: не пересчитывать дорогое начальное значение на каждом рендере (ленивая инициализация) и не терять первоначальное состояние между рендерами. Разберём обе.

Ленивая инициализация — не вычислять заново

Аргумент useState вычисляется при каждом вызове компонента (хотя используется только в первом):

const [data, setData] = useState(parseHugeJson(raw));   // parse на каждый рендер!
const [data2, setData2] = useState(() => parseHugeJson(raw)); // parse один раз

Функция-инициализатор выполняется единожды при монтировании — дорогая подготовка (localStorage, парсинг, генерация) не повторяется. Сам ререндер от этого не исчезает (и не должен) — исчезает лишняя работа в нём.

Сохранить «первоначальное» значение для сравнения

Если нужно помнить исходное состояние (например, для «сбросить изменения» или «есть ли правки»), его фиксируют один раз:

const Form = ({ user }) => {
  const [draft, setDraft] = useState(user);      // рабочая копия
  const initialRef = useRef(user);               // первоначальное, не вызывает рендеров

  const isDirty = draft !== initialRef.current;
  const reset = () => setDraft(initialRef.current);
};

useRef хранит значение между рендерами без ререндеров при доступе; state — для того, что отображается и меняется.

Если цель — «изменять без ререндера»

Значения, не влияющие на UI, в state держать не нужно вовсе — useRef: изменение ref.current компонент не перерисовывает. А сеттер useState с тем же значением (Object.is) ререндер и так пропустит.

Коротко для собеседования

Дорогое начальное значение — через ленивый инициализатор useState(() => compute()): вычислится один раз. Первоначальное состояние для сравнения/сброса — зафиксировать в useRef (живёт между рендерами, не triggers ререндер). Всё, что не отображается, — вообще в ref, а не в state.

Другие вопросы раздела