Какое initial value можно передать в useState в React?
Что проверяют
Понимаете ли вы, что начальное значение нужно React только на первом рендере, и отличаете ли значение от функции-инициализатора. Следом часто спрашивают, почему состояние не обновилось, когда поменялся проп.
Как отвечать
Передать можно значение любого типа:
- примитивы: число, строку,
boolean,null. Без аргумента состояние равноundefined; - объекты и массивы:
useState({ page: 1 }),useState([]). Меняют их только новым объектом,setItems([...items, x]): мутация на месте ререндер не вызовет, React сравнивает ссылки черезObject.is; - функция: React считает её инициализатором, вызывает без аргументов на первом рендере и сохраняет результат. Дальше её не вызывают.
Разницу стоит показать кодом:
useState(parseList(raw)):parseListвыполняется на каждом рендере, результат просто выбрасывается;useState(() => parseList(raw))илиuseState(parseList), если аргументы не нужны: тяжёлая работа выполнится один раз.
Отсюда следствие: положить в состояние саму функцию напрямую нельзя, React её вызовет. Нужна обёртка useState(() => handler). Инициализатор должен быть чистым: в Strict Mode в режиме разработки React вызывает его дважды, чтобы поймать побочные эффекты.
В TypeScript тип выводится из начального значения, поэтому для «пока пусто» его указывают явно: useState<User | null>(null).
Начальное значение читается только при монтировании. Если оно взято из пропа и проп поменялся, состояние останется старым. Сбрасывают его через key на компоненте или вовсе не копируют проп в состояние, а вычисляют значение при рендере.
Где спотыкаются
Пишут useState(expensive()) и уверены, что это считается один раз. Скобки делают вызов на каждом рендере.
Ждут, что useState(props.value) будет следить за пропом, и добавляют useEffect для синхронизации копии. Получается лишний рендер и расхождение данных.
Пытаются хранить колбэк как useState(fn) и получают в состоянии результат его вызова вместо самой функции.