自定义挂钩中的setState调用不会更新状态

时间:2019-09-23 02:21:23

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

我正在尝试建立一个基本的自定义钩子,以在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;```

1 个答案:

答案 0 :(得分:2)

您的示例作品...

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

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

但是,setOrientation中的setDimensions将调度更改并将其传达给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

因此,如果您还想在orientation中使用useEffect,则可以将其声明为依赖项:

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

以这个Codesandbox为例,希望它能使您澄清一些事情!