【问题标题】:How does React Hooks useCallback "freezes" the closure?React Hooks 如何使用回调“冻结”闭包?
【发布时间】:2023-03-31 19:17:02
【问题描述】:

我想知道 React 如何在使用 useCallback 钩子(以及其他钩子)时“冻结”闭包,然后仅在将钩子内使用的变量传递给 inputs 时更新它们参数。

我知道“冻结”可能不是很清楚,所以我创建了一个 REPL.it 来说明我的意思:https://repl.it/repls/RudeMintcreamShoutcast。打开代码后,打开 Web 浏览器控制台并开始单击 count 按钮。

如果外部值与内部值相比,对于同一个变量,如果它们在同一个闭包下并引用相同的东西,为什么它们是不同的?我不熟悉 React 代码库,所以我想我在这里遗漏了一个底层实现细节,但我试图思考它如何工作几分钟,但无法很好地理解 React 是如何工作的实现这一目标。

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    第一次渲染组件时,useCallback 钩子会将传递的函数作为其参数并将其存储在幕后。当您调用回调时,它将调用您的函数。到目前为止,一切顺利。

    第二次渲染组件时,useCallback 钩子会检查你传入的依赖关系。如果它们没有改变,你传入的函数将被完全忽略!当您调用回调时,它将调用您在第一次渲染时传入的函数,该函数仍然引用该时间点的相同值。这与您作为依赖项传入的值无关 - 它只是普通的 JavaScript 闭包!

    当依赖关系发生变化时,useCallback 钩子会获取你传入的函数并替换它存储的函数。当你调用回调时,它会调用 new 版本的函数。

    所以换句话说,没有“冻结”/有条件更新的变量 - 它只是存储一个函数然后重新使用它,没有比这更花哨的了 :)

    编辑:这是一个演示纯 JavaScript 的示例:

    // React has some component-local storage that it tracks behind the scenes.
    // useState and useCallback both hook into this.
    //
    // Imagine there's a 'storage' variable for every instance of your
    // component.
    const storage = {};
    
    function useState(init) {
      if (storage.data === undefined) {
        storage.data = init;
      }
      
      return [storage.data, (value) => storage.data = value];
    }
    
    function useCallback(fn) {
      // The real version would check dependencies here, but since our callback
      // should only update on the first render, this will suffice.
      if (storage.callback === undefined) {
        storage.callback = fn;
      }
    
      return storage.callback;
    }
    
    function MyComponent() {
      const [data, setData] = useState(0);
      const callback = useCallback(() => data);
    
      // Rather than outputting DOM, we'll just log.
      console.log("data:", data);
      console.log("callback:", callback());
    
      return {
        increase: () => setData(data + 1)
      }
    }
    
    let instance = MyComponent(); // Let's 'render' our component...
    
    instance.increase(); // This would trigger a re-render, so we call our component again...
    instance = MyComponent();
    
    instance.increase(); // and again...
    instance = MyComponent();

    【讨论】:

    • 所以,我关心的正是“从那个时间点开始仍然引用相同的值”。为什么 useCallback 与此不同? gist.github.com/rdsedmundo/f393f1201336c26625022215b77af711
    • @EdmundoRodrigues:啊,我明白你的意思了——在你的例子中,a 在你调用你的回调函数时都引用了同一个范围内的同一个变量。在您的原始代码中,由于整个函数已在渲染时重新运行,count 在回调内外引用不同的变量。等一下,我会试着编一个更有代表性的例子。
    • @EdmundoRodrigues:我添加了一个示例,它演示了在没有任何特定于 React 的代码的情况下发生了什么 - 希望它会有所帮助!
    • 啊,真可惜。我的整个困惑是因为我认为 React 实际上是在创建组件的“实例”。但是功能组件上似乎没有这样的东西。谢谢!
    • @EdmundoRodrigues:组件有一个“实例”,但是当您使用功能组件/挂钩时,它只是在幕后进行管理,而不是以this 的形式向您公开。您实际上只是自己编写 render 方法并告诉 React 为您处理其余的 :)
    猜你喜欢
    • 2020-01-22
    • 1970-01-01
    • 1970-01-01
    • 2020-02-15
    • 2021-06-16
    • 1970-01-01
    • 2020-07-13
    • 1970-01-01
    • 2020-01-07
    相关资源
    最近更新 更多