反应:为什么从 useRef 更改 ref 的当前值不会在这里触发 useEffect

Joj*_*oji 6 reactjs

我有一个问题useRef:如果我添加ref.current到 的依赖项列表中useEffect,并且当我更改了 的值时ref.currentuseEffect将不会触发内部的回调。

例如:

export default function App() {
  const myRef = useRef(1);
  useEffect(() => {
    console.log("myRef current changed"); // this only gets triggered when the component mounts
  }, [myRef.current]);
  return (
    <div className="App">
      <button
        onClick={() => {
          myRef.current = myRef.current + 1;
          console.log("myRef.current", myRef.current);
        }}
      >
        change ref
      </button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

不应该是当useRef.current变化时,里面的东西useEffect就会运行吗?

另外,我知道我可以用useState在这里。这不是我要问的。而且我也知道ref在重新渲染期间保持参照不变,所以它不会改变。但我不是在做类似的事情

 const myRef = useRef(1);
  useEffect(() => {
    //...
  }, [myRef]);
Run Code Online (Sandbox Code Playgroud)

我将current值放在 dep 列表中,因此应该会发生变化。

Dim*_*nis 30

我知道我有点晚了,但既然你似乎没有接受任何其他答案,我想我也应该尝试一下,也许这就是对你有帮助的。

难道不应该是当 useRef.current 更改时,useEffect 中的内容就会运行吗?

简短的回答,

在 React 中导致重新渲染的唯一原因如下:

  1. 组件内的状态更改(通过useStateuseReducer钩子)
  2. 道具变更
  3. 如果组件未记忆或以其他方式引用相同,则父渲染(由于 1. 2. 或 3.)(有关此兔子洞的更多信息,请参阅此问题和答案)

让我们看看您共享的代码示例中发生了什么:

export default function App() {
  const myRef = useRef(1);
  useEffect(() => {
    console.log("myRef current changed"); // this only gets triggered when the component mounts
  }, [myRef.current]);
  return (
    <div className="App">
      <button
        onClick={() => {
          myRef.current = myRef.current + 1;
          console.log("myRef.current", myRef.current);
        }}
      >
        change ref
      </button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

初始渲染

  • myRef被设置为{current: 1}
  • 效果回调函数被注册
  • React 元素被渲染
  • React 刷新到 DOM(这是您在屏幕上看到结果的部分)
  • 效果回调函数被执行,“myRef currentchanged”被打印在控制台中

就是这样。以上3个条件都不满足,所以不再重新渲染。

但是当您单击按钮时会发生什么?你运行一个效果。此效果会更改currentref 对象的值,但不会触发会导致重新渲染的更改(1. 2. 或 3. 中的任何一个)。您可以将裁判视为“效果”的一部分。它们不遵守 React 组件的生命周期,也不影响它。

如果组件现在要重新渲染(例如,由于其父级重新渲染),则会发生以下情况:

正常渲染

  • myRef被设置为{current: 1}- 引用的设置仅发生在初始渲染时,因此该行const myRef = useRef(1);没有进一步的效果。
  • 效果回调函数被注册
  • React 元素被渲染
  • 如有必要,React 会刷新到 DOM
  • 之前效果的清理函数被执行(这里没有)
  • 效果回调函数被执行,“myRef currentchanged”被打印在控制台中。如果您有一个console.log(myRef.current)内部效果回调,您现在会看到打印的值为 2(或者无论您在初始渲染和此渲染之间按下按钮多少次)

总而言之,由于 ref 更改(ref 是一个值,甚至是对 DOM 元素的引用)而触发重新渲染的唯一方法是使用 ref 回调(如本答案中所建议的那样)并内部该回调将 ref 值存储到 提供的状态useState


Gal*_*lit 12

使用useCallBack代替,这是 React 文档的解释:

\n
\n

在此示例中,我们没有选择 useRef,因为对象引用不会通知我们当前引用值的更改。使用回调引用可以确保即使子组件稍后显示测量的节点(例如响应单击),我们仍然会在父组件中收到有关它的通知,并且可以更新测量值。

\n

请注意,我们将 [] 作为依赖项数组传递给 useCallback。这确保了我们的 ref 回调在重新渲染之间不会发生变化,因此 React 不会不必要地调用它。

\n
\n
function MeasureExample() {\n  const [height, setHeight] = useState(0);\n\n  const measuredRef = useCallback(node => {\n    if (node !== null) {\n      setHeight(node.getBoundingClientRect().height);\n    }\n  }, []);\n\n  return (\n    <>\n      <h1 ref={measuredRef}>Hello, world</h1>\n      <h2>The above header is {Math.round(height)}px tall</h2>\n    </>\n  );\n}\n
Run Code Online (Sandbox Code Playgroud)\n


cyn*_*l3r 5

https://reactjs.org/docs/hooks-reference.html#useref

请记住, useRef 不会在其内容更改时通知您。改变 .current 属性不会导致重新渲染。如果您想在 React 将 ref 附加或分离到 DOM 节点时运行一些代码,您可能需要改用回调 ref。