According to the documentation for Immer, data not originating from the state will not be drafted(immer - pitfalls). However, I encountered an issue where it seems like such data is being drafted contrary to the documentation.
Here's an example:
import produce from "immer";
let baseState = {
value: { x: 1 },
t: "a",
};
let someVal = { x: 2 };
let nextState = produce(baseState, (draft) => {
draft.value = someVal;
draft.value.x = 1; // Results in false
});
console.log("baseState", JSON.stringify(baseState));
console.log("nextState", JSON.stringify(nextState));
console.log("isEqual", baseState === nextState);
As per my understanding, drafting implies copying the data to a temporary place, so the baseState and nextState values should be equal in this scenario. However, the comparison at the end (baseState === nextState) results in false.
Why baseState and nextState are not equal in this context despite the documentation suggesting that data not originating from the state should not be drafted?
Object variables in Javascript hold a reference to the value of the object.
This means if you have an object variable
const objectA = { propertyA: 1};
and you create a new variableconst objectB = objectA;
both variables contain the same reference to the value of the object.What this means is that if you mutate the object referenced by
objectA
you are also mutating the object referenced byobjectB
.The limitation you have referenced in Immer is talking about it's inability to automatically draft random values you throw at it on the fly in a produce function.
What does that mean?!
The last time I looked Immer was using a Proxy (or their own implementation of one) to track changes in the produce function.
When you assign an object reference (
someVal
) to a property on the draft the proxy is recording the change you to want make and the property path you wish to make it to.Immer is only recording this as a change of reference, it has recorded nothing (NOTHING) about the value that is being referenced.
When the produce function completes Immer will run through and create a new object with the changes that were recorded.
It will see the reference change only, it will know nothing of the changes made to the value that is being referenced in between it being recorded and the state being updated.
Also
console.log("isEqual", baseState === nextState);
should return false when any change is made to the draft during the produce function.The point of immer is to turn those changes into a new immutable object, this new immutable object will have a different reference to the original (
===
between objects is a reference comparison not a value comparison).