Skip to content

Commit e0b7c01

Browse files
authored
fix: #768 immerable field being lost during patch value cloning (#771)
1 parent 2c2f30e commit e0b7c01

File tree

4 files changed

+61
-6
lines changed

4 files changed

+61
-6
lines changed

__tests__/empty.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import {
22
produce,
33
produceWithPatches,
44
setUseProxies,
5-
enableAllPlugins
5+
enableAllPlugins,
6+
immerable,
7+
applyPatches
68
} from "../src/immer"
7-
import {DRAFT_STATE} from "../src/internal"
9+
import {DRAFT_STATE, Patch} from "../src/internal"
810

911
enableAllPlugins()
1012

@@ -149,3 +151,53 @@ function createBaseState() {
149151
}
150152
return data
151153
}
154+
155+
describe("#768", () => {
156+
class Stock {
157+
[immerable] = true
158+
159+
constructor(public price: number) {}
160+
161+
pushPrice(price: number) {
162+
this.price = price
163+
}
164+
}
165+
166+
type State = {
167+
stock: Stock
168+
}
169+
170+
test("bla", () => {
171+
// Set up conditions to produce the error
172+
const errorProducingPatch = [
173+
{
174+
op: "replace",
175+
path: ["stock"],
176+
value: new Stock(200)
177+
}
178+
] as Patch[]
179+
180+
// Start with modified state
181+
const state = {
182+
stock: new Stock(100)
183+
}
184+
185+
expect(state.stock.price).toEqual(100)
186+
expect(state.stock[immerable]).toBeTruthy()
187+
// Use patch to "replace" stocks
188+
debugger
189+
const resetState: State = applyPatches(state, errorProducingPatch)
190+
expect(state.stock.price).toEqual(100)
191+
expect(resetState.stock.price).toEqual(200)
192+
expect(resetState.stock[immerable]).toBeTruthy()
193+
194+
// Problems come in when resetState is modified
195+
const updatedState = produce(resetState, draft => {
196+
draft.stock.pushPrice(300)
197+
})
198+
expect(state.stock.price).toEqual(100)
199+
expect(updatedState.stock.price).toEqual(300)
200+
expect(updatedState.stock[immerable]).toBeTruthy()
201+
expect(resetState.stock.price).toEqual(200)
202+
})
203+
})

__tests__/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"compilerOptions": {
44
"lib": ["es2015"],
55
"strict": true,
6-
"noUnusedLocals": false
6+
"noUnusedLocals": false,
7+
"target": "ES5"
78
}
89
}

src/core/immerClass.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export class Immer implements ProducersFns {
185185
this.useProxies_ = value
186186
}
187187

188-
applyPatches(base: Objectish, patches: Patch[]) {
188+
applyPatches<T extends Objectish>(base: Objectish, patches: Patch[]): T {
189189
// If a patch replaces the entire state, take that replacement as base
190190
// before applying patches
191191
let i: number
@@ -200,12 +200,12 @@ export class Immer implements ProducersFns {
200200
const applyPatchesImpl = getPlugin("Patches").applyPatches_
201201
if (isDraft(base)) {
202202
// N.B: never hits if some patch a replacement, patches are never drafts
203-
return applyPatchesImpl(base, patches)
203+
return applyPatchesImpl(base, patches) as any
204204
}
205205
// Otherwise, produce a copy of the base state.
206206
return this.produce(base, (draft: Drafted) =>
207207
applyPatchesImpl(draft, patches.slice(i + 1))
208-
)
208+
) as any
209209
}
210210
}
211211

src/plugins/patches.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {immerable} from "../immer"
12
import {
23
ImmerState,
34
Patch,
@@ -287,6 +288,7 @@ export function enablePatches() {
287288
if (isSet(obj)) return new Set(Array.from(obj).map(deepClonePatchValue))
288289
const cloned = Object.create(Object.getPrototypeOf(obj))
289290
for (const key in obj) cloned[key] = deepClonePatchValue(obj[key])
291+
if (has(obj, immerable)) cloned[immerable] = obj[immerable]
290292
return cloned
291293
}
292294

0 commit comments

Comments
 (0)