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.
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?
Also I know I can use useState
here. 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. 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.
https://reactjs.org/docs/hooks-reference.html#useref
Keep in mind that useRef doesn't notify you when its content changes. Mutating the.current property doesn't cause a re-render. 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.
Ok so I think what you're missing here is that changing a ref's value doesn't cause a re-render. So if it doesn't cause re-renders, then the function doesn't get run again. Which means useEffect
isn't run again. 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. 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:
We didn't choose useRef in this example because an object ref doesn't notify us about changes to the current ref value. 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.
Note that we pass [] as a dependency array to useCallback. This ensures that our ref callback doesn't change between the re-renders, and so React won't call it unnecessarily.
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?
Short answer, no .
The only things that cause a re-render in React are the following:
useState
or useReducer
hooks)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}
And that's it. None of the above 3 conditions is satisfied, so no more rerenders.
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.). You can think of refs as part of an "effect". They do not abide by the lifecycle of React components and they do not affect it either.
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);
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) 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
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.