javascript - 在 state 和 ref 中保持相同的值是 React 中的常见做法吗?

标签 javascript reactjs react-hooks use-state use-ref

我的 React 应用程序使用 setTimeout()setInterval()。在它们内部,我需要访问状态值。正如我们所知,闭包一旦创建就绑定(bind)到它们的上下文,因此在 setTimeout()/setInterval() 中使用状态值不会使用最新值。

为了简单起见,假设我的组件是这样定义的:

import { useState, useEffect, useRef } from 'react';

const Foo = () => {
    const [number, setNumber] = useState(0);
    const numberRef = useRef(number);

    // Is this common? Any pitfalls? Can it be done better?
    numberRef.current = number;

    useEffect(
        () => setInterval(
            () => {
                if (numberRef.current % 2 === 0) {
                    console.log('Yay!');
                }
            },
            1000
        ),
        []
    );

    return (
        <>
            <button type="button" onClick={() => setNumber(n => n + 1)}>
                Add one
            </button>
            <div>Number: {number}</div>
        </>
    );
};

我总共想出了 3 个想法来实现这一目标,其中任何一个是公认的模式吗?

  1. 在每次渲染时将状态值分配给 ref,就像上面一样:

    numberRef.current = number;
    

    好处是代码非常简单。

  2. 使用 useEffect() 注册 number 的变化:

    useEffect(
        () => numberRef.current = number,
        [number]
    );
    

    这个看起来更像 React,但真的有必要吗?当可以使用第 1 点的简单赋值时,它实际上不会降低性能吗?

  3. 使用自定义 setter :

    const [number, setNumberState] = useState(0);
    const numberRef = useRef(number);
    
    const setNumber = value => {
        setNumberState(value);
        numberRef.current = value;
    };
    

在 state 和 ref 中具有相同的值是 React 的常见模式吗?这 3 种方式中的任何一种是否出于某种原因比其他方式更受欢迎?有哪些替代方案?

最佳答案

2021-10-17 编辑:

因为这看起来很常见,所以我想将整个逻辑包装成一个直观的

useInterval(
  () => console.log(`latest number value is: ${number}`), 
  1000
)

其中 useInterval 参数始终可以“访问”最新状态。

CodeSandbox 中玩了一会儿之后,我意识到没有其他人没有考虑过解决方案。

你瞧,Dan Abramov 本人发表了一篇博文,为我们的问题提供了精确的解决方案 https://overreacted.io/making-setinterval-declarative-with-react-hooks/

我强烈建议阅读完整的博客,因为它描述了声明式 React 编程和命令式 API 之间不匹配的普遍问题。 Dan 还解释了他(逐步)开发完整解决方案的过程,该解决方案能够在需要时更改间隔延迟。

Here (CodeSandbox)你可以在你的特定情况下测试它。


原始答案:

1.

numberRef.current = number;

我会避免这种情况,因为我们通常希望在 useEffect 而不是 render 方法中进行状态/引用更新。

在这种特殊情况下,它没有太大影响,但是,如果您要添加另一个状态并对其进行修改 -> 将触发渲染周期 -> 这段代码也会运行并无缘无故地分配一个值(数字值不会改变)。

2.

useEffect(
    () => numberRef.current = number,
    [number]
);

恕我直言,这是您提供的所有 3 种方式中最好的方式。这是一种将托管状态“同步”到可变 ref 对象的干净/声明方式。

3.

const [number, setNumberState] = useState(0);
const numberRef = useRef(number);

const setNumber = value => {
    setNumberState(value);
    numberRef.current = value;
};

在我看来,这并不理想。其他开发人员习惯于 React API,可能看不到您的自定义 setter,而是在添加更多逻辑时使用默认的 setNumberState,期望它被用作“真实来源”-> setInterval 不会得到最新数据。

关于javascript - 在 state 和 ref 中保持相同的值是 React 中的常见做法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69483390/

相关文章:

reactjs - 测试自定义 Hook 的返回值

javascript - jQuery animate 链接在运行时更改动画速度

php - Google Analytics - 我可以将脚本放在页脚中吗?

javascript - React 路由器基本实现

javascript - 如何在 react 应用程序中重新加载页面(状态)

javascript - 无法使用 useState React 将 Prop 设置为状态

javascript - 在博客文章列表中移动动态 div

javascript - 为什么非 ASCII 字符显示为奇怪的符号?

javascript - 如何检查对象的键上是否有空值?

reactjs - DataCloneError : The object could not be cloned. [火狐浏览器]