Suggestion: Add notify method to allow signal based reactivity
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 29k
- Forks
- 881
- PR merge metrics
- No merged PRs in 30d
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];
}
Contributor guide
No contributing guide indexed for this repository
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.
Research direction
Start in createProxyProxy and inspect the ProxyState initialization, then trace objectTraps.set and originalObjectTrapsSet. Compare the proposed optional notification hook with the createDraft and finishDraft flow and the Svelte example; done means signal-based consumers can receive draft changes without affecting existing behavior when the hook is unset.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100