在渲染期间而不是在 useEffect 中更改引用的值是否安全?

jtb*_*des 10 reactjs react-hooks react-strictmode

我用来useRef保存 prop 的最新值,以便稍后可以在异步调用的回调(例如 onClick 处理程序)中访问它。我使用 ref 而不是放入valueuseCallback 依赖项列表,因为我预计该值会频繁更改(当使用新的组件重新渲染时value),但 onClick 处理程序很少被调用,因此它是不值得在每次值更改时为元素分配新的事件侦听器。

\n
function MyComponent({ value }) {\n  const valueRef = useRef(value);\n  valueRef.current = value;  // is this ok?\n\n  const onClick = useCallback(() => {\n    console.log("the latest value is", valueRef.current);\n  }, []);\n\n  ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

React Strict Mode的文档让我相信执行副作用通常render()是不安全的。

\n
\n

由于上述方法[包括类组件render()和函数组件体]可能会被多次调用,因此\xe2\x80\x99重要的是它们不包含副作用。忽略此规则可能会导致各种问题,包括内存泄漏和无效的应用程序状态。

\n
\n

事实上,当我使用引用访问旧值时,我在严格模式下遇到了问题。

\n

我的问题是:是否担心从渲染函数分配的“副作用” ?valueRef.current = value例如,是否存在回调会收到过时值(或来自尚未提交的“未来”渲染的值)的情况?

\n

我能想到的一种替代方案是useEffect确保在组件渲染后更新引用,但从表面上看,这看起来没有必要。

\n
function MyComponent({ value }) {\n  const valueRef = useRef(value);\n  useEffect(() => {\n    valueRef.current = value;  // is this any safer/different?\n  }, [value]);\n\n  const onClick = useCallback(() => {\n    console.log("the latest value is", valueRef.current);\n  }, []);\n\n  ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Ret*_*sam 7

\n

例如,是否存在回调会收到过时值(或来自尚未提交的“未来”渲染的值)的情况?

\n
\n

括号是主要关注点。

\n

当前之间存在一一对应关系render。(即承诺)

\n

但很长一段时间以来,React 团队一直在谈论可能开始更新的“并发模式” (render被调用),但随后会被更高优先级的更新中断。

\n

在这种情况下,如果引用在被取消的渲染中进行了更新,则引用最终可能与渲染组件的实际状态不同步。

\n

很长一段时间以来,这一直是假设,但刚刚宣布,一些并发模式更改将以一种选择加入的方式出现在 React 18 中,startTransition。(也许还有其他一些)

\n
\n

实际上,这在多大程度上是一个实际问题?很难说。 startTransition是选择加入的,所以如果您不使用它,您可能是安全的。无论如何,许多参考更新将相当“安全”。

\n

但如果可以的话,最好还是谨慎行事。

\n
\n

更新:现在,react.dev 文档还说不应该这样做:

\n
\n

不要写或读ref.current除初始化外,这使得您的组件\xe2\x80\x99s 的行为变得不可预测。

\n
\n

上面的初始化意味着这样的模式

\n
function Video() {\n  const playerRef = useRef(null);\n  if (playerRef.current === null) {\n    playerRef.current = new VideoPlayer();\n  }\n  ....\n
Run Code Online (Sandbox Code Playgroud)\n