【问题标题】:setState call inside a custom hook is not updating the state自定义挂钩内的 setState 调用未更新状态
【发布时间】: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)不会改变如果你在函数内部重新定义它。

标签: javascript reactjs react-native closures react-hooks


【解决方案1】:

您的示例有效...

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

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

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

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

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-20
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    • 2020-03-05
    • 2022-12-06
    • 2023-02-15
    相关资源
    最近更新 更多