-
-
Notifications
You must be signed in to change notification settings - Fork 866
Open
Labels
Description
🚀 Feature Proposal
Add an optional notfy_ function to the ProxyState that defaults to null. So that the state in createProxyProxy looks like this:
const state: ProxyState = {
type_: isArray ? ArchType.Array : (ArchType.Object as any),
// Track which produce call this is associated with.
scope_: parent ? parent.scope_ : getCurrentScope()!,
// True for both shallow and deep changes.
modified_: false,
// Used during finalization.
finalized_: false,
// Track which properties have been assigned (true) or deleted (false).
assigned_: {},
// The parent draft state.
parent_: parent,
// The base state.
base_: base,
// The base proxy.
draft_: null as any, // set below
// The base copy with any updated values.
copy_: null,
// Called by the `produce` function.
revoke_: null as any,
isManual_: false,
// new optional method for change notification; defaulting to null
notify_: null
}and then updating the object trap to wrap the set function:
export const objectTraps: ProxyHandler<ProxyState> = {
...
set(state: ProxyObjectState, prop: string /* strictly not, but helps TS */, value) {
const res = originalObjectTrapsSet(state,prop,value)
if(state.notify_) state.notify_()
return res
}Motivation
The immer Proxy does not play nice with the svelte 5 rune proxy. Adding the notify_ method would allow adding reactivity to drafts.
I can't see any downsides in having the notify_ method as it has no noticeable performance impact when not used, is backwards compatible and opt in.
Can this be solved in user-land code?
I have not found a satisfying user land solution. Doesn't mean there isn't one
Example
A simple implementation that allows the default svelte 5 bind:value on inputs. Comments on svelte specific code; would work similar with other signal based systems.
const DRAFT_IMMER_PROXY = Symbol.for('og-immer-proxy');
function makeDraft<T extends object>(data: T) {
const draft = createDraft(data);
//update is a svelte internal signal to notify dependent values
const subscribe = createSubscriber((update) => {
draft[DRAFT_STATE].notify_ = () => {
update();
};
});
const traps: ProxyHandler<Draft<T>> = {
get(target, p, receiver) {
if (p == DRAFT_IMMER_PROXY) {
return draft;
}
// create svelte subscription
subscribe();
return Reflect.get(target, p, receiver);
}
};
const proxy = new Proxy(draft, traps);
return proxy;
}
function commit(draft: Draft<any>) {
const immerDrat = draft[DRAFT_IMMER_PROXY];
if (!immerDrat) return;
let fwd;
let bwd;
const res = finishDraft(immerDrat, (p, u) => {
fwd = p;
bwd = u;
});
return [res, fwd, bwd];
}Andrew-Morozko