【问题标题】:Value from useRef React hook has initial value only来自 useRef React 钩子的值只有初始值
【发布时间】:2020-04-16 10:50:31
【问题描述】:

我正在尝试使用挂钩来存储状态。这个钩子暴露了对值的引用和一个简单的 mutator 函数。我想有一个不同的更新钩子,它使用所说的突变器。虽然在更新程序中值是正确的,但如果我还有另一个钩子,则该值始终是初始值。这是一个重现问题的示例。

import React, { useEffect, useRef } from "react";

function useCounter() {
  const counter = useRef<number>(0);

  const increment = () => {
    counter.current++;
  };

  return [counter, increment] as const;
}

function useUpdate() {
  const [counter, increment] = useCounter();

  console.log("rendering update");

  return () => {
    increment();

    console.log(counter.current);
  };
}

function useLoop() {
  const update = useUpdate();
  const [counter] = useCounter();

  console.log("rendering loop");

  return () => {
    update();
    console.log(counter.current);
  };
}

export default function App() {
  const loop = useLoop();

  useEffect(() => {
    const id = window.setInterval(loop, 1000);

    return () => {
      window.clearInterval(id);
    };
  }, [loop]);

  return <div />;
}

在 useUpdate 内部,计数器的值是正确的,但在 useLoop 内部,它始终为 0。我使用 ref 是因为当计数器更改时不需要重新运行挂钩,而且它是一个对象,从循环返回的函数应该通过引用来获取它,并且始终具有最新的值,但事实并非如此。 我的目标是使用类似的模式来编写一个结合 p5js 的模拟。这个问题与 p5 完全无关,所以我只是嘲笑了它的更新功能。 我尝试使用 useState、useCallback 但没有任何效果。

Codesandbox

我是钩子新手,这种模式可能完全错误。我欢迎有关使用钩子进行高级分解的提示。

谢谢!

编辑:

问题在于 useCounter 运行了两次并创建了两个不同的、不相关的实例。这是我提出的状态分解解决方案:

import React, { useEffect, useRef } from "react";

function useCounter() {
  const counter = useRef<number>(0);

  const increment = () => {
    counter.current++;
  };

  return { counter, increment };
}

function useUpdate({ counter, increment }) {

  console.log("rendering update");

  return () => {
    increment();

    console.log(counter.current);
  };
}

function useLoop(state) {
  const update = useUpdate(state);

  console.log("rendering loop");

  return () => {
    update();
    console.log(state.counter.current);
  };
}

export default function App() {

  const state = useCounter();
  const loop = useLoop(state);

  useEffect(() => {
    const id = window.setInterval(loop, 1000);

    return () => {
      window.clearInterval(id);
    };
  }, [loop]);

  return <div />;
}

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    useLoop 中,您实例化了一个新版本的counter,它与您之前声明的不同。 要解决这个问题,您需要从 useUpdate 返回 counter 所以:

    function useUpdate() {
      const [counter, increment] = useCounter();
    
      console.log("rendering update");
    
      const incrementFunc = () => {
        increment();
    
        console.log(counter.current);
      };
      return [incrementFunc, counter]
    }
    
    function useLoop() {
      const [update, counter] = useUpdate();
      // const [counter] = useCounter(); this will be a new counter
    
      console.log("rendering loop");
    
      return () => {
        update();
        console.log(counter.current);
      };
    }
    
    

    【讨论】:

    • 建议的解决方案不是我想要的,但它帮助我理解了问题。澄清一下,问题是 useCounter 在 Update 和 Loop 中运行了两次。
    【解决方案2】:

    我认为这是一个范围问题。您在 useLoop 中初始化一个新的计数器变量,该变量独立于 useUpdate 中的计数器变量。您必须从钩子返回变量和它的 mutator 函数。我对 useUpdate 和 useLoop 钩子进行了以下更改,它似乎按您的预期工作。

    function useUpdate() {
      const [counter, increment] = useCounter();
    
      console.log("rendering update");
    
      // return both counter and its mutator function
      return [counter, () => {
        increment();
    
        console.log(counter.current);
      }];
    }
    
    function useLoop() {
      // use counter variable exported from useUpdate hook
      const [counter, update] = useUpdate();
    
      console.log("rendering loop");
    
      return () => {
        update();
        console.log(counter.current);
      };
    }
    

    不确定这是否有帮助,我也是 Hooks 的新手,也许有更多经验的人可以回答得更好。

    【讨论】:

    • 谢谢,但这并没有真正的帮助,因为这种模式会因为多个钩子而变得非常混乱。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-07
    相关资源
    最近更新 更多