I have implemented two hooks. They are useWatch1 and useWatch2, respectively.
The function of useWatch is to execute a cb function when the listening value changes.
The user first clicks the ADD COUNT
button a few times, and then the count value will increase. Then the user clicks the ADD ID
button, and the count
value will be set to 0
. useWatch2
meets expectations, but useWatch1
cannot. I would like to know why?
import { useRef, useState } from "react";
function useWatch1(current, cb) {
const isFirstRender = useRef(true);
const prevRef = useRef(null);
if (isFirstRender.current) {
prevRef.current = current;
isFirstRender.current = false;
} else if(prevRef.current !== current){
prevRef.current = current;
cb();
}
}
function useWatch2(current, cb) {
const [prev, setPrev] = useState(current);
if (current !== prev) {
setPrev(current);
cb();
}
}
function MyCpm({ id }) {
const [count, setCount] = useState(0);
// useWatch1(id, () => {
// setCount(0);
// });
useWatch2(id, () => {
setCount(0);
});
console.log("render", count);
return (
<>
<button
onClick={() => {
setCount((c) => c + 1);
}}
>
ADD COUNT
</button>
<span>count: {count}</span>
<span>-----</span>
<span>id: {id}</span>
</>
);
}
function App() {
const [id, setId] = useState(0);
return (
<>
<button
onClick={() => {
setId((id) => id + 1);
}}
>
ADD ID
</button>
<MyCpm id={id}></MyCpm>
</>
);
}
export default App;
2
Answers
You’ve overcomplicated the situation – there’s documented ability to "reset" state (unmount/mount) components using keys
useWatch1
would work if you do not use React Strict Mode. With Strict Mode enabled, React will (re)render the component twice to allow the programmer to find accidental impurities in the component. The first rerender will setprevRef.current
to the new value, so the values will no longer differ on the second rerender, which is the one that is used to update the DOM.useWatch2
has no such issue as React will simply ignore state updates on the first (discarded) rerender.