Skip to content

Commit

Permalink
fix: avoid deep copying unnecessarily in current (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
francescotescari authored Jul 23, 2024
1 parent dbde1bf commit aadbe1e
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 15 deletions.
54 changes: 39 additions & 15 deletions src/current.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,51 @@ export function handleReturnValue<T extends object>(options: {
}
}

function getCurrent(target: any) {
const UNCHANGED = Symbol('Unchanged');

function getCurrent(target: any): any | typeof UNCHANGED {
const proxyDraft = getProxyDraft(target);
if (!isDraftable(target, proxyDraft?.options)) return target;
if (!isDraftable(target, proxyDraft?.options)) return UNCHANGED;
const type = getType(target);
if (proxyDraft && !proxyDraft.operated) return proxyDraft.original;
if (proxyDraft) proxyDraft.finalized = true;
let currentValue: any;
try {
currentValue =
type === DraftType.Map
? new Map(target)
: type === DraftType.Set
? Array.from(proxyDraft!.setMap!.values()!)
: shallowCopy(target, proxyDraft?.options);
} finally {
if (proxyDraft) proxyDraft.finalized = false;
function ensureShallowCopy() {
if (!currentValue || currentValue === target)
currentValue =
type === DraftType.Map
? new Map(target)
: type === DraftType.Set
? Array.from(proxyDraft!.setMap!.values()!)
: shallowCopy(target, proxyDraft?.options);
}

if (proxyDraft) {
// It's a proxy draft, let's create a shallow copy eagerly
proxyDraft.finalized = true;
try {
ensureShallowCopy();
} finally {
proxyDraft.finalized = false;
}
} else {
// It's not a proxy draft, let's use the target directly and let's see
// lazily if we need to create a shallow copy
currentValue = target;
}

forEach(currentValue, (key, value) => {
if (proxyDraft && isEqual(get(proxyDraft.original, key), value)) return;
set(currentValue, key, getCurrent(value));
const newValue = getCurrent(value);
if (newValue !== UNCHANGED) {
ensureShallowCopy();
set(currentValue, key, newValue);
}
});
return type === DraftType.Set ? new Set(currentValue) : currentValue;
return type === DraftType.Set
? new Set(currentValue)
: currentValue === target
? UNCHANGED
: currentValue;
}

/**
Expand All @@ -105,5 +128,6 @@ export function current<T extends object>(target: T): T {
if (!isDraft(target)) {
throw new Error(`current() is only used for Draft, parameter: ${target}`);
}
return getCurrent(target);
const current = getCurrent(target);
return current === UNCHANGED ? target : current;
}
10 changes: 10 additions & 0 deletions test/immer/__tests__/current.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,15 @@ function runTests(name) {
expect(c).toBeInstanceOf(Counter);
});
});

it("won't deep copy unchanged values unnecessarily", () => {
const obj = { k: 42 };
const base = { x: { y: { z: obj } } };
produce(base, (draft) => {
draft.x = { y: { z: obj } };
const c = current(draft);
expect(c.x.y.z).toBe(obj);
});
});
});
}

0 comments on commit aadbe1e

Please sign in to comment.