Skip to content

Commit 6e0b2da

Browse files
authored
Send patches to state subscribers (#363)
Subscribers of the new BaseController will now also receive a patch for each state change, in addition to a copy of the new state. Patches can make it easier to efficiently serialize state updates. This was done as part of the controller redesign (#337).
1 parent ded054c commit 6e0b2da

File tree

2 files changed

+11
-9
lines changed

2 files changed

+11
-9
lines changed

src/BaseControllerV2.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ describe('BaseController', () => {
7575
});
7676

7777
expect(listener1.callCount).toEqual(1);
78-
expect(listener1.firstCall.args).toEqual([{ count: 1 }]);
78+
expect(listener1.firstCall.args).toEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]);
7979
expect(listener2.callCount).toEqual(1);
80-
expect(listener2.firstCall.args).toEqual([{ count: 1 }]);
80+
expect(listener2.firstCall.args).toEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]);
8181
});
8282

8383
it('should inform a subscriber of each state change once even after multiple subscriptions', () => {
@@ -91,7 +91,7 @@ describe('BaseController', () => {
9191
});
9292

9393
expect(listener1.callCount).toEqual(1);
94-
expect(listener1.firstCall.args).toEqual([{ count: 1 }]);
94+
expect(listener1.firstCall.args).toEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]);
9595
});
9696

9797
it('should no longer inform a subscriber about state changes after unsubscribing', () => {

src/BaseControllerV2.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import { produce } from 'immer';
1+
import { enablePatches, produceWithPatches } from 'immer';
22

33
// Imported separately because only the type is used
44
// eslint-disable-next-line no-duplicate-imports
5-
import type { Draft } from 'immer';
5+
import type { Draft, Patch } from 'immer';
6+
7+
enablePatches();
68

79
/**
810
* State change callbacks
911
*/
10-
export type Listener<T> = (state: T) => void;
12+
export type Listener<T> = (state: T, patches: Patch[]) => void;
1113

1214
/**
1315
* Controller class that provides state management and subscriptions
@@ -67,10 +69,10 @@ export class BaseController<S extends Record<string, any>> {
6769
* object. Return a new state object or mutate the draft to update state.
6870
*/
6971
protected update(callback: (state: Draft<S>) => void | S) {
70-
const nextState = produce(this.internalState, callback) as S;
71-
this.internalState = nextState;
72+
const [nextState, patches] = produceWithPatches(this.internalState, callback);
73+
this.internalState = nextState as S;
7274
for (const listener of this.internalListeners) {
73-
listener(nextState);
75+
listener(nextState as S, patches);
7476
}
7577
}
7678

0 commit comments

Comments
 (0)