Ваш пример работает ...
setOrientation
фактически изменит значение состояния orientation
, а useDeviceDimensions
вернет измененное orientation
.Проблема скорее в том, где вы поместили оператор console.log
- и вы правы, это связано с замыканием (и его областью действия), переданным в useEffect
.
useEffect
вызывается только один раз с вашим заданным обратным вызовом.Этот обратный вызов является закрытием и может получить доступ к orientation
из useState
.Во время создания замыкания переменная orientation
будет иметь значение null
.Поскольку никогда не создается новое замыкание ([]
зависимости в useEffect
), оно всегда будет печатать значение null
, когда включенный прослушиватель изменений запускается.
Однако setOrientation
внутри setDimensions
отправит и сообщит об изменении в React.Процесс выглядит следующим образом:
- Закрытие
useEffect
вызывается один раз при первом рендеринге. - Через некоторое время запускается событие
change
. setDimensions
внутри вашего замыкания всегда печатает одно и то же значение из внешней области видимости функции orientation
(которая во время построения имеет значение null
). setOrientation
вызывается, React внутренне обновляет состояние и запускаетновый цикл рендеринга в родительском компоненте. useDeviceDimensions
снова вызывается из родительского компонента, теперь он имеет и возвращает новое состояние orientation
. - Новое изменение инициируется,перейдите к 3 снова.
Точка 4 работает в замыкании, потому что React имеет внутренний список «ячеек памяти» для каждого компонента, где он может читать и обновлять недавнее состояние, поэтому обновления работаютчерез область закрытия (см. здесь для получения дополнительной информации).Сторона чтения orientation
- это устаревшая переменная, если хотите, которая имеет ссылку на первое значение состояния, которое React хранит изначально.
... но лучше объявить useEffect
безопасным способом
Ваш хук useEffect
использует состояние, которое вы не упоминаете в его массиве зависимостей.Что React docs говорит по этому поводу:
Если вы используете эту оптимизацию, убедитесь, что массив включает в себя все значения из области действия компонента (например, реквизиты и состояние), которые изменяются со временем и используютсяэффект.В противном случае ваш код будет ссылаться на устаревшие значения из предыдущих рендеров. Ссылка
Так что, если вы также хотите использовать orientation
в useEffect
, вы можете объявить его как зависимость:
useEffect(() => {
...
}, [orientation]);
Посмотритев этом Codesandbox для примера, надеюсь, это немного прояснит ситуацию!