useRef React挂钩中的值仅具有初始值

时间:2020-04-16 10:50:31

标签: reactjs react-hooks

我正在尝试使用钩子来存储状态。该钩子公开了对该值的引用和一个简单的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 />;
}

2 个答案:

答案 0 :(得分:1)

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

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);
  };
}

答案 1 :(得分:0)

我认为这是一个范围问题。您可以在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还是陌生的,也许有更多经验的人可以回答得更好。