Есть ли проблемы при инициализации useState функцией в React и как их решить
Что проверяют
Понимаете ли вы разницу между useState(compute()) и useState(compute) и что начальное значение используется только при монтировании. Следом обычно спрашивают, что делать, если начальное значение зависит от пропса, который потом меняется.
Как отвечать
- ленивая инициализация: если передать функцию, React вызовет её только на первом рендере и сохранит результат.
useState(() => readDraft())прочитает хранилище один раз. АuseState(expensive())вызываетexpensiveна каждом рендере, результат просто выбрасывается; - ссылка, а не вызов:
useState(compute)иuseState(() => compute())работают одинаково. Инициализатор должен быть чистым и без аргументов; - Strict Mode: в разработке React вызывает инициализатор дважды, чтобы поймать побочные эффекты. Запросам, подпискам и мутациям внутри не место;
- функция как значение:
useState(myCallback)не положит функцию в состояние, а вызовет её. НужноuseState(() => myCallback), то же касаетсяsetState; - асинхронность: инициализатор синхронный, вернёте промис, в состоянии окажется промис. Данные грузят в эффекте, через TanStack Query или через
useс Suspense; - SSR: обращение к
localStorageилиwindowв инициализаторе упадёт на сервере, а значение, отличное от серверного, даст ошибку гидратации. Такое читают после монтирования.
Про пропсы: изменение пропса состояние не пересоздаёт. Варианты по порядку предпочтения: вычислить значение при рендере без состояния вовсе, сбросить компонент через key, в крайнем случае скорректировать состояние во время рендера, сравнив с предыдущим пропсом.
Где спотыкаются
Утверждают, что функция-инициализатор пересчитывается на каждом рендере. Всё наоборот: пересчитывается выражение, переданное без обёртки.
Синхронизируют пропс с состоянием через useEffect(() => setValue(prop), [prop]). Это лишний рендер со старым значением, и документация React в разделе «You Might Not Need an Effect» прямо советует так не делать.
Принимают двойной вызов инициализатора в Strict Mode за баг и отключают режим, вместо того чтобы убрать из инициализатора побочные эффекты.