自定义挂钩内的 setState 调用未更新状态

Posted

技术标签:

【中文标题】自定义挂钩内的 setState 调用未更新状态【英文标题】:setState call inside a custom hook is not updating the state 【发布时间】:2020-01-23 01:56:37 【问题描述】:

我正在尝试设置一个基本的自定义挂钩来返回 React Native 中的设备方向。我旁边有一个功能代码示例,但我不知道为什么这不起作用。我认为这是某种关闭/范围问题,但显然我不知道。

我知道有更简单的方法来获取设备方向,但我想知道为什么这不起作用以及如何使它起作用。

当前行为:调用setOrientation 不会更改orientation 的值。 预期行为:调用setOrientation 会更新orientation 的值。

一旦发生这种情况,我认为钩子会正常工作,因为每次Dimensions 更新时它都会返回null

[更新:为清楚起见进行了编辑]


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;```

【问题讨论】:

useDeviceDimensions 函数返回的对象的属性永远不会改变,这与 React 无关。只是因为你从不改变返回的对象,你重新定义了它包含的一些变量。 只能在这里猜测,您所说的“这不起作用”是什么意思 - orientation 应该可以正常工作。您不会在维度更改时更新/调用 setWidthsetHeight @Titus 你能详细说明一下吗?我不明白我的代码如何重新定义状态变量而不是更新它们。 @ford04 请查看更新后的代码 - 我取出了未使用的部分,以便更清楚我要做什么。 我的意思是,从外观上看,您使用的是useDeviceDimensions的返回值,如果是这样的话,这个返回值(orientation)不会改变如果你在函数内部重新定义它。 【参考方案1】:

您的示例有效...

setOrientation 实际上会改变orientation 的状态值,useDeviceDimensions 会返回改变后的orientation。问题在于您放置 console.log 语句的位置 - 您是对的,这与传递给 useEffect 的闭包(及其范围)有关。

useEffect 使用您定义的回调调用 only once。这个回调是一个闭包,可以从useState 访问orientation。在创建闭包时,orientation 变量将具有值null。因为永远不会创建新的闭包(useEffect 中的[] 依赖项),所以当触发包含的更改侦听器时,它将始终打印值null

但是,setDimensions 内部的 setOrientation 将分派并将更改传达给 React。流程是这样的:

    useEffect 闭包在第一次渲染时被调用一次。 稍后会触发change 事件。 闭包内的setDimensions 始终从函数外部作用域orientation 变量(在构造时具有值null)打印相同的值。 setOrientation 被调用,React 内部更新状态并在父组件中触发新的渲染周期。 useDeviceDimensions 再次从父组件调用,它现在拥有并返回新的 orientation 状态。 触发了新的更改,再次执行 3。

第 4 点适用于闭包,因为 React 为每个组件都有一个“内存单元”的内部列表,它可以在其中读取和更新最近的状态,因此更新可以在闭包范围内工作(有关更多信息,请参阅 here )。读取端 orientation 是一个陈旧的变量,如果你愿意的话,它只是对 React 最初存储的第一个状态值的引用。

...但最好以安全的方式声明useEffect

您的 useEffect 钩子使用了您在其依赖项数组中未提及的状态。 React 文档对此有何评论:

如果您使用此优化,请确保数组包含组件范围内的所有值(例如 props 和 state),这些值会随时间变化并被效果器使用。否则,您的代码将引用以前渲染中的陈旧值。 Link

所以如果你还想在useEffect 中使用orientation,你可以将它声明为依赖:

 useEffect(() => 
  ...
  , [orientation]);

以Codesandbox 为例,希望它能澄清一点!

【讨论】:

以上是关于自定义挂钩内的 setState 调用未更新状态的主要内容,如果未能解决你的问题,请参考以下文章

对 useEffect 挂钩内的多个 setState() 调用做出反应批量更新

调用 setState 后 ReactJS 元素未更新

尽管状态发生了变化,但自定义钩子不会触发组件重新渲染

Wordpress 自定义帖子操作挂钩

ReactJS 状态未更新在 setState 回调中

用于电容器存储的定制挂钩