javascript - 自定义挂钩内的 setState 调用未更新状态
问题描述
我正在尝试设置一个基本的自定义挂钩以在 React Native 中返回设备方向。我旁边有一个功能代码示例,但我不知道为什么这不起作用。我认为这是某种关闭/范围问题,但显然我不知道。
我知道有更简单的方法来获取设备方向,但我想知道为什么这不起作用以及如何使它起作用。
当前行为:调用setOrientation
不会更改orientation
. 预期行为:调用setOrientation
会更新orientation
.
一旦发生这种情况,我认为钩子会正常工作,因为它null
每次Dimensions
更新都会返回。
[更新:为清楚起见进行了编辑]
import { Dimensions, } from 'react-native';
function useDeviceDimensions() {
const [orientation, setOrientation] = useState(null);
useEffect(() => {
const setDimensions = (e) => {
console.log('running', orientation); // null
const {width, height} = e.window;
setOrientation(width > height ? 'landscape' : 'portrait');
};
Dimensions.addEventListener('change', setDimensions);
return () => Dimensions.removeEventListener('change', setDimensions);
}, [])
return orientation;
}
export default useDeviceDimensions;```
解决方案
您的示例有效...
setOrientation
实际上会改变 state 的值orientation
并useDeviceDimensions
返回改变的orientation
. 问题在于您放置console.log
语句的位置-您是对的,这与传递给useEffect
.
useEffect
仅使用您定义的回调调用一次。这个回调是一个闭包,可以orientation
从useState
. 在创建闭包时,orientation
变量将具有值null
。因为永远不会创建新的闭包([]
依赖项),所以当触发包含的更改侦听器时useEffect
,它将始终打印值。null
然而,setOrientation
insidesetDimensions
会将更改分派并传达给 React。流程是这样的:
- 闭包在第
useEffect
一次渲染时被调用一次。 - 稍后某个
change
事件被触发。 setDimensions
在您的闭包内部始终从函数外部范围orientation
变量(在构造时具有 valuenull
)打印相同的值。setOrientation
被调用时,React 在内部更新状态并在父组件中触发新的渲染周期。useDeviceDimensions
从父组件再次调用,它现在拥有并返回新orientation
状态。- 触发了一个新的变化,再继续 3。
第 4 点适用于闭包,因为 React 为每个组件都有一个“内存单元”的内部列表,它可以在其中读取和更新最近的状态,因此更新可以在闭包范围内工作(有关更多信息,请参见此处)。读取端orientation
是一个陈旧的变量,如果你愿意的话,它只是对 React 最初存储的第一个状态值的引用。
...但最好useEffect
以安全的方式声明
您的useEffect
钩子使用您在其依赖项数组中未提及的状态。React 文档对此有何评论:
如果您使用此优化,请确保数组包含组件范围内的所有值(例如 props 和 state),这些值随时间变化并被效果使用。否则,您的代码将引用以前渲染中的陈旧值。关联
因此,如果您还想使用orientation
in useEffect
,可以将其声明为依赖项:
useEffect(() => {
...
}, [orientation]);
看看这个Codesandbox的例子,希望它能澄清一点!
推荐阅读
- sql - 每个部门的最高薪水(也是相同的薪水)
- python - 如何使用 python 生成特定的嵌套 JSON
- visual-studio-2015 - 安装 Visual Studio 2017 后找不到 Visual Studio 2015 midl
- android - 如何为线性布局添加卡片视图效果
- postgresql - 将具有多列的 CSV 导入 pgAdmin v4.1
- javascript - 如何根据子数组元素状态拆分数组?
- javascript - 如何使对话框关闭与生成的视图一起使用?
- mysql - 在mysql中加入两个表时遇到SequelizeEagerLoadingError
- javascript - 当它必须发生而不触发任何事件时,究竟在哪里做数组操作
- regex - 如何在 Dart 中替换字符串中间的空格?