Does produce() perform strict equality check (===) when setting values in receipt?
d6u opened this issue Β· comments
πββ Question
Thanks in advance!
I have below example:
const newData = produce(
oldData,
(draft) => {
draft.tasks[0].title = draft.tasks[0].title;
}
);
My questions are:
- Since
draft.tasks[0].title
is assigned todraft.tasks[0].title
, will that causeoldData === newData
to be true? - How about when
draft.tasks[0].title
is"Hello"
and I dodraft.tasks[0].title = "Hello"
? - This is expected behavior that I can rely on or undefined behavior that I shouldn't rely on?
I expect that if I'm replacing the a object or array, produce
is probably not going to perform deep comparison before applying the changes/patches. But I'm wondering if I can rely on immer to not generate a new object if primitive values are the same. Right now based on my observation, oldData === newData
is indeed true.
Link to repro
N/A
Environment
We only accept questions against the latest Immer version.
- Immer version: 10.0.2
- I didn't call
setUseProxies
or import it.
A correct immutable update will make copies of every level of nesting.
If you want to to update state.some.nested.field = 123
, it needs to make:
- a copy of
nested
with a newfield
value - a copy of
some
that has the newnested
value - a copy of
state
that has the newsome
value
So in a normal update, newData !== oldData
, because the new object tree did have to make copies of tasks[0]
, tasks
, and draft
(aka data
).
However, it looks in this example like you're just setting the exact same value. In that case, yes, I think Immer is smart enough to detect that nothing actually changed, and the overall result will be a no-op, no updates or copies performed.
A correct immutable update will make copies of every level of nesting.
If you want to to update
state.some.nested.field = 123
, it needs to make:
- a copy of
nested
with a newfield
value- a copy of
some
that has the newnested
value- a copy of
state
that has the newsome
valueSo in a normal update,
newData !== oldData
, because the new object tree did have to make copies oftasks[0]
,tasks
, anddraft
(akadata
).However, it looks in this example like you're just setting the exact same value. In that case, yes, I think Immer is smart enough to detect that nothing actually changed, and the overall result will be a no-op, no updates or copies performed.
Got it. Thanks for explaining. Can I rely on this behavior in future versions? In order words, is this a documented and expected behavior that the Immer maintainers will ensure to not break?
As far as I know, yes.
Thanks! That answered me question.
@markerikson please have a look.
In above case, nothing change, ref is the same,
but in below caseοΌnot meeting expectations. nothing change, but the ref is not the same.
const baseState = {
a: 1,
};
const newState = produce(
baseState,
(draft) => {
draft.a = 2;
draft.a = 1;
},
(patch) => {
// tobe: []
console.log(patch);
}
);
// tobe: false
console.log( newState === baseState);