Как в 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.