Shrinking an array with `length =` and growing it back loses the patches for the truncated indices
Maintainers usually reply within 4 days
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 75/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript
- Domain
- backend
Research direction
Start in src/plugins/patches.ts, especially generateArrayPatches_, and trace how shrinking a draft array changes copy_ and assigned_. Add the provided regression test for shrinking and regrowing length, then run the patch-related tests. Done means forward patches reproduce the recipe result and inverse patches restore the base.
Written by the indexing model from the issue text.
Description
🐛 Bug Report
With enablePatches(), if a recipe shrinks a draft array by assigning .length and then grows it back to at least its original length, no replace patch (and no inverse patch) is produced for the truncated indices. Replaying the patches on the base gives a different state than the recipe, and the inverse patches do not restore the base. The same recipe written with splice(0) produces correct patches.
Link to repro
Not applicable: no CodeSandbox account; the script below and the unit test at the end reproduce it.
To Reproduce
npm i [email protected], save as t.mjs, run node t.mjs:
import { produceWithPatches, applyPatches, enablePatches } from "immer";
enablePatches();
const base = [1];
const [result, patches, inverse] = produceWithPatches(base, d => {
d.length = 0; // truncate index 0
d[1] = 2; // grow again
});
console.log(result);
console.log(patches);
console.log(inverse);
console.log(applyPatches(base, patches));
console.log(applyPatches(result, inverse));
const [r2, p2, i2] = produceWithPatches([1, 2], d => { d.length = 0; d.length = 2; });
console.log(r2, p2, i2);
Observed behavior
[ <1 empty item>, 2 ]
[ { op: 'add', path: [ 1 ], value: 2 } ]
[ { op: 'remove', path: [ 1 ] } ]
[ 1, 2 ]
[ <1 empty item> ]
[ <2 empty items> ] [] []
applyPatches(base, patches) keeps the stale 1 at index 0, and applyPatches(result, inverse) does not bring it back. The second recipe changes both elements but produces no patches at all.
Expected behavior
The same patches as for d.splice(0); d[1] = 2, which immer already gets right:
[
{ op: 'replace', path: [ 0 ], value: undefined },
{ op: 'add', path: [ 1 ], value: 2 }
] [
{ op: 'replace', path: [ 0 ], value: 1 },
{ op: 'remove', path: [ 1 ] }
]
so that applyPatches(base, patches) gives [ undefined, 2 ] and applyPatches(result, inverse) gives [ 1 ].
generateArrayPatches_ in src/plugins/patches.ts only emits a replace for an index below base.length when assigned_.get(i) is set (or allIndicesReassigned_). Assigning a smaller length truncates copy_ without marking the dropped indices as assigned, so they are skipped once the array is long again.
Unit test:
test("length shrink then grow produces replace patches", () => {
const base = [1]
const [result, patches, inverse] = produceWithPatches(base, d => {
d.length = 0
d[1] = 2
})
expect(applyPatches(base, patches)).toEqual(result)
expect(applyPatches(result, inverse)).toEqual(base)
})
Environment
We only accept bug reports against the latest Immer version.
- Immer version: 11.2.9 (also main 75a290e, built from source), Node.js v26.13.0
- I filed this report against the latest version of Immer
- Occurs with
setUseProxies(true) - Occurs with
setUseProxies(false)(ES5 only)
Credit: https://ucsbplse.github.io/
- Dominant language
- JavaScript
- Stars
- 29k
- Forks
- 905
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 22
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from immerjs/immer
-
Difficulty 3/5 1-2 days Newbie friendliness 35/100
Maintainers usually reply within 4 days
-
Docs: Chinese (zh-CN) website translation is outdated vs English docsMay be free again @lijiayou0728 claimed this 91 days ago, and no pull request is open. Open
Difficulty 4/5 3-5 days Newbie friendliness 55/100
immerjs/immer#1242 · 5 comments ·
Maintainers usually reply within 4 days
-
question
Difficulty 5/5 Over a week Newbie friendliness 35/100
Maintainers usually reply within 4 days
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
immerjs/immer#1217 · 1 comment ·
Maintainers usually reply within 4 days
-
[Feature Request] Support for "Splice" patches in arrays for CRDT/Y.js integration (and others)Openproposal
Difficulty 5/5 Over a week Newbie friendliness 25/100
immerjs/immer#1202 · 2 comments · 1 reaction ·
Maintainers usually reply within 4 days
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Automattic/node-canvas#2633 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
capricorn86/happy-dom#2594 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 79/100
microsoft/monaco-editor#5575 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Maintainers usually reply within 1 day