Skip to content

Producers not eventually producing a modified value still spawn a new value #360

@ymeine

Description

@ymeine

🐛 Bug Report

Using on an array a producer which first modifies a value but later on reverts it to the original one still keeps the draft marked as "modified" and therefore spawns a new array, making the output not strictly equal to the input.

To Reproduce

Execute that:

const immer = require('immer');

const original = [1];
const modified = immer.produce(original, function (array) {
	array[0] = 2; // modify it
	array[0] = 1; // revert it back to original value
});
console.log(modified === original); // outputs "false"

Expected behavior

From the concept of immutability, I expect conceptually equal values to be strictly equal in practice. In other words: a reference pointing to an array of value [1] should always be strictly equal to another reference pointing to an array of value [1].

I understand that using an API like immer, it is difficult to track that down across different calls to produce. e.g.

const original = [1];

const modified1 = immer.produce(original, function (array) { array[0] = 2; });
console.log(modified1 === original); // outputs "false" as expected

const modified2 = immer.produce(modified1, function (array) { array[0] = 1; });
console.log(modified2 === original); // outputs "false" while we could expect "true", since they actually have the same value ([1]), but I understand this is difficult to track down

However, I would have expected it to be possible inside the same call to produce:

const original = [1];

const attempt1 = immer.produce(original, function (array) { array[0] = 1; });
console.log(attempt1 === original); // outputs "true" as expected

const attempt2 = immer.produce(
	original,
	function (array) {
		array[0] = 2; // marks the value as "modified"
		array[0] = 1; // doesn't mark it back to "not modified"
	},
	function (patches) {
		console.log(patches); // this outputs an empty list, and that is expected!
		// This can be used as workaround outside to reassign the output of `produce` to the base value if patches list is empty,
		// so that I can achieve my goal of having the same reference
	}
);
console.log(attempt2 === original); // outputs "false" and that's not really expected

Link to repro

Immer sandbox - CodeSandbox (open the console).

Environment

  • Immer version: 2.1.5
  • Occurs with setUseProxies(true)
  • Occurs with setUseProxies(false) (ES5 only)

PS: identified piece of code: immer/proxy.js at master · immerjs/immer, once modified once, no chance to check if value is back to the original. Anyways, there is markUnchanged counterpart, and with complex hierarchies I understand marking back as "not modified" could be complex though, since it doesn't necessarily means the parent is not modified anymore

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions