简体   繁体   English

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

[英]React: why is that changing the current value of ref from useRef doesn't trigger the useEffect here

I have a question about useRef : if I added ref.current into the dependency list of useEffect , and when I changed the value of ref.current , the callback inside of useEffect won't get triggered.我有一个关于useRef的问题:如果我将ref.current添加到useEffect的依赖项列表中,并且当我更改 ref.current 的值时, ref.current内部的useEffect将不会被触发。

for example:例如:

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

Shouldn't it be when useRef.current changes, the stuff in useEffect gets run?不应该是当useRef.current发生变化时, useEffect中的东西就会运行吗?

Also I know I can use useState here.我也知道我可以在这里使用useState This is not what I am asking.这不是我要问的。 And also I know that ref stay referentially the same during re-renders so it doesn't change.而且我知道ref在重新渲染期间保持引用相同,因此它不会改变。 But I am not doing something like但我没有做类似的事情

 const myRef = useRef(1);
  useEffect(() => {
    //...
  }, [myRef]);

I am putting the current value in the dep list so that should be changing.我将current值放在 dep 列表中,因此应该会发生变化。

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

Keep in mind that useRef doesn't notify you when its content changes.请记住,当其内容发生变化时,useRef 不会通知您。 Mutating the.current property doesn't cause a re-render.改变 .current 属性不会导致重新渲染。 If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.如果你想在 React 将 ref 附加或分离到 DOM 节点时运行一些代码,你可能需要使用回调 ref 来代替。

Ok so I think what you're missing here is that changing a ref's value doesn't cause a re-render.好的,所以我认为您在这里缺少的是更改 ref 的值不会导致重新渲染。 So if it doesn't cause re-renders, then the function doesn't get run again.因此,如果它不会导致重新渲染,那么 function 就不会再次运行。 Which means useEffect isn't run again.这意味着useEffect不会再次运行。 Which means it never gets a chance to compare the values.这意味着它永远没有机会比较这些值。 If you trigger a re-render with a state change you will see that the effect will now get run.如果您使用 state 更改触发重新渲染,您将看到效果现在将运行。 So try something like this:所以尝试这样的事情:

export default function App() {
  const [x, setX] = useState();
  const myRef = useRef(1);
  useEffect(() => {
    console.log("myRef current changed"); // this only gets triggered when the component mounts
  }, [myRef.current]);
  return (
    <button
        onClick={() => {
          myRef.current = myRef.current + 1;
          // Update state too, to trigger a re-render
          setX(Math.random());
          console.log("myRef.current", myRef.current);
        }}
      >
        change ref
      </button>
  );
}

Now you can see it will trigger the effect.现在你可以看到它会触发效果。

use useCallBack instead, here is the explanation from React docs:改用useCallBack ,这是 React 文档的解释:

We didn't choose useRef in this example because an object ref doesn't notify us about changes to the current ref value.在此示例中,我们没有选择 useRef,因为 object ref 不会通知我们当前 ref 值的更改。 Using a callback ref ensures that even if a child component displays the measured node later (eg in response to a click), we still get notified about it in the parent component and can update the measurements.使用回调 ref 可确保即使子组件稍后显示测量节点(例如响应单击),我们仍会在父组件中收到有关它的通知并可以更新测量值。

Note that we pass [] as a dependency array to useCallback.请注意,我们将 [] 作为依赖数组传递给 useCallback。 This ensures that our ref callback doesn't change between the re-renders, and so React won't call it unnecessarily.这确保了我们的 ref 回调在重新渲染之间不会改变,因此 React 不会不必要地调用它。

function MeasureExample() {
  const [height, setHeight] = useState(0);

  const measuredRef = useCallback(node => {
    if (node !== null) {
      setHeight(node.getBoundingClientRect().height);
    }
  }, []);

  return (
    <>
      <h1 ref={measuredRef}>Hello, world</h1>
      <h2>The above header is {Math.round(height)}px tall</h2>
    </>
  );
}

I know I am a little late, but since you don't seem to have accepted any of the other answers I'd figure I'd give it a shot too, maybe this is the one that helps you.我知道我有点晚了,但是由于您似乎没有接受任何其他答案,所以我想我也会试一试,也许这对您有帮助。

Shouldn't it be when useRef.current changes, the stuff in useEffect gets run?不应该是当 useRef.current 发生变化时, useEffect 中的东西就会运行吗?

Short answer, no .简短的回答,

The only things that cause a re-render in React are the following:在 React 中导致重新渲染的唯一原因如下:

  1. A state change within the component (via the useState or useReducer hooks)组件内的 state 更改(通过useStateuseReducer挂钩)
  2. A prop change道具变化
  3. A parent render (due to 1. 2. or 3.) if the component is not memoized or otherwise referentially the same (see this question and answer for more info on this rabbit hole)如果组件未记忆或引用相同,则父渲染(由于 1. 2. 或 3.)(有关此兔子洞的更多信息,请参阅问题和答案)

Let's see what happens in the code example you shared:让我们看看您分享的代码示例中发生了什么:

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

Initial render初始渲染

  • myRef gets set to {current: 1} myRef设置为{current: 1}
  • The effect callback function gets registered效果回调 function 被注册
  • React elements get rendered React 元素被渲染
  • React flushes to the DOM (this is the part where you see the result on the screen) React 刷新到 DOM(这是您在屏幕上看到结果的部分)
  • The effect callback function gets executed, "myRef current changed" gets printed in the console效果回调 function 被执行,“myRef current changed”被打印在控制台中

And that's it.就是这样。 None of the above 3 conditions is satisfied, so no more rerenders.以上 3 个条件都不满足,所以不再重新渲染。

But what happens when you click the button?但是当你点击按钮时会发生什么? You run an effect .你运行一个效果 This effect changes the current value of the ref object, but does not trigger a change that would cause a rerender (any of either 1. 2. or 3.).此效果会更改 ref object 的current值,但不会触发会导致重新渲染的更改(1. 2. 或 3. 中的任何一个)。 You can think of refs as part of an "effect".您可以将 refs 视为“效果”的一部分。 They do not abide by the lifecycle of React components and they do not affect it either.它们不遵守 React 组件的生命周期,也不影响它。

If the component was to rerender now (say, due to its parent rerendering), the following would happen:如果组件现在要重新渲染(例如,由于其父级重新渲染),则会发生以下情况:

Normal render正常渲染

  • myRef gets set to {current: 1} - Set up of refs only happens on initial render, so the line const myRef = useRef(1); myRef被设置为 {current: 1} - refs 的设置只发生在初始渲染时,所以行const myRef = useRef(1); has no further effect.没有进一步的影响。
  • The previous effect's cleanup function gets executed (here there is none)上一个效果的清理 function 被执行(这里没有)
  • The effect callback function gets registered效果回调 function 被注册
  • React elements get rendered React 元素被渲染
  • React flushes to the DOM if necessary如有必要,React 刷新到 DOM
  • The effect callback function gets executed, "myRef current changed" gets printed in the console.效果回调 function 被执行,“myRef current changed”被打印在控制台中。 If you had a console.log(myRef.current) inside the effect callback, you would now see that the printed value would be 2 (or however many times you have pressed the button between the initial render and this render)如果您在效果回调中有一个console.log(myRef.current) ,您现在会看到打印的值为 2(或者无论您在初始渲染和此渲染之间按下按钮多少次)

All in all, the only way to trigger a re-render due to a ref change (with the ref being either a value or even a ref to a DOM element) is to use a ref callback (as you have done here) and inside that callback store the ref value to a state provided by useState .总而言之,由于 ref 更改(ref 是一个值甚至是 DOM 元素的 ref)而触发重新渲染的唯一方法是使用 ref 回调(就像您在此处所做的那样)和内部该回调将 ref 值存储到 useState 提供的useState中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 React:如何从 useEffect 内部返回 useRef.current.value? - React: How to return useRef.current.value from inside useEffect? 使用 React 钩子(useEffect 和 useRef)——为什么 ref 在复选框更改时更新? - Using React hooks (useEffect and useRef) - Why ref gets updated on checkbox changes? 与脉轮反应不会触发 useEffect - React with chakra doesn't trigger useEffect 用ref触发反应dropzone不起作用 - trigger react dropzone with ref doesn't work 为什么 useEffect 不会在文档更改时触发? - Why useEffect doesn't trigger on document change? 为什么 useEffect 中的 setState 不更新提供给它的 ref.current 值? - Why setState in useEffect in not updating to the ref.current value supplied to it? 为什么 React ref element.current 使用 useRef 钩子在 React 组件中返回 null? - Why React ref element.current returns null in React component using useRef hook? 使用 react useRef() -- 为什么我的 ref 是 null? - Using react useRef() --why my ref is null? 为什么反应`useRef`钩子将对象存储在`current`属性中? 为什么它不能直接存储在 ref 对象中? - Why react `useRef` hook stores the objects in `current` property? Why cant it store directly in the ref object? 为什么更改 state 中的值不会触发 REACT 中输入元素的 onChange - Why changing value in state doesn't trigger onChange of input element in REACT
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM