According to https://react.dev/reference/react/useMemo, it says useMemo evaluate deps value with Object.is()
function, so if deps values are non primitive like array or object, useMemo calculates the value every render.
import { useMemo, useState } from "react";
export default function App() {
const temp = { a: 1 };
const [a, setA] = useState({ a: 1 });
const [aa, setAA] = useState(1);
const obj = useMemo(() => {
console.log("obj");
return { b: a.a };
}, [a]);
const obj2 = useMemo(() => {
console.log("obj2");
return { b: temp.a };
}, [temp]);
return (
<div className="App">
<button onClick={() => setA((prev) => ({ a: prev.a + 1 }))}>
button {a.a}
</button>
<div>{obj.b}</div>
<div>{obj2.b}</div>
<button onClick={() => setAA((prev) => prev + 1)}>premitive {aa}</button>
</div>
);
}
When I click either the first or second button, the console output obj2
, which makes sense because variable temp
is an object and useMemo calculate value every render.
However, When I click the second button, the console does not output obj
even though variable a
is an object state.
Why does it happen? I assumed the first useMemo also calculate the value every render, but somehow not
2
Answers
React state is an object actually just like the ordinary JavaScript object. Except that react state can be tracked by the virtual dom and any change to the state can be detected. This is not so with ordinary JavaScript objects. This leads to more complex principles.
Any state passed to useMemo should also be trackable just like in the virtual dom. But not ordinary objects. useMemo cannot track changes to ordinary objects.
So while passing dependencies to useMemo, if the dependency is a primitive data type stored in state, it will not be recomputed (for a lack of a better term) but if it is an object stored in state, it will be recomputed (also for a lack of a better term) on every render.
How about values not stored in state?
Well you guessed it. Values not stored in state cannot be tracked by useMemo.
The documentation describes them as non reactive values.
You’re misunderstanding this part. For non-primitive values,
Object.is()
compares whether they’re the same reference. So if you doObject.is({a: 1}, {a: 1})
that will returnfalse
.In this case though,
useState()
returns the same reference on the next render if it’s not been modified, which is the case when you click the second button because clicking the second button does not modifya
.If you instead pass
{...a}
touseMemo()
, which will create a shallow clone ofa
, theuseMemo()
will be reevaluated on every render.