immerjs / immer

Create the next immutable state by mutating the current one

Home Page:https://immerjs.github.io/immer/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

  1. Since draft.tasks[0].title is assigned to draft.tasks[0].title, will that cause oldData === newData to be true?
  2. How about when draft.tasks[0].title is "Hello" and I do draft.tasks[0].title = "Hello"?
  3. 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 new field value
  • a copy of some that has the new nested value
  • a copy of state that has the new some 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 new field value
  • a copy of some that has the new nested value
  • a copy of state that has the new some 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.

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);