[英]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 中导致重新渲染的唯一原因如下:
useState
or useReducer
hooks)useState
或useReducer
挂钩) 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>
);
}
myRef
gets set to {current: 1}
myRef
设置为{current: 1}
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:如果组件现在要重新渲染(例如,由于其父级重新渲染),则会发生以下情况:
myRef
gets set to
{current: 1}
const myRef = useRef(1);
myRef
被设置为
{current: 1}
const myRef = useRef(1);
has no further effect.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.