diff --git a/packages/core/src/dom/store/slices/buffer.ts b/packages/core/src/dom/store/slices/buffer.ts index 1e9b679e..951310f3 100644 --- a/packages/core/src/dom/store/slices/buffer.ts +++ b/packages/core/src/dom/store/slices/buffer.ts @@ -22,9 +22,8 @@ export const bufferSlice = createSlice()({ }), subscribe: ({ target, update, signal }) => { - const sync = () => update(); - listen(target, 'progress', sync, { signal }); - listen(target, 'emptied', sync, { signal }); + listen(target, 'progress', update, { signal }); + listen(target, 'emptied', update, { signal }); }, request: {}, diff --git a/packages/core/src/dom/store/slices/playback.ts b/packages/core/src/dom/store/slices/playback.ts index 00055c24..d791fa2b 100644 --- a/packages/core/src/dom/store/slices/playback.ts +++ b/packages/core/src/dom/store/slices/playback.ts @@ -28,12 +28,11 @@ export const playbackSlice = createSlice()({ }), subscribe: ({ target, update, signal }) => { - const sync = () => update(); - listen(target, 'play', sync, { signal }); - listen(target, 'pause', sync, { signal }); - listen(target, 'ended', sync, { signal }); - listen(target, 'playing', sync, { signal }); - listen(target, 'waiting', sync, { signal }); + listen(target, 'play', update, { signal }); + listen(target, 'pause', update, { signal }); + listen(target, 'ended', update, { signal }); + listen(target, 'playing', update, { signal }); + listen(target, 'waiting', update, { signal }); }, request: { diff --git a/packages/core/src/dom/store/slices/source.ts b/packages/core/src/dom/store/slices/source.ts index 42da8c13..8c0e3363 100644 --- a/packages/core/src/dom/store/slices/source.ts +++ b/packages/core/src/dom/store/slices/source.ts @@ -22,11 +22,10 @@ export const sourceSlice = createSlice()({ }), subscribe: ({ target, update, signal }) => { - const sync = () => update(); - listen(target, 'canplay', sync, { signal }); - listen(target, 'canplaythrough', sync, { signal }); - listen(target, 'loadstart', sync, { signal }); - listen(target, 'emptied', sync, { signal }); + listen(target, 'canplay', update, { signal }); + listen(target, 'canplaythrough', update, { signal }); + listen(target, 'loadstart', update, { signal }); + listen(target, 'emptied', update, { signal }); }, request: { diff --git a/packages/core/src/dom/store/slices/time.ts b/packages/core/src/dom/store/slices/time.ts index 44b7c47d..e00704f8 100644 --- a/packages/core/src/dom/store/slices/time.ts +++ b/packages/core/src/dom/store/slices/time.ts @@ -22,12 +22,11 @@ export const timeSlice = createSlice()({ }), subscribe: ({ target, update, signal }) => { - const sync = () => update(); - listen(target, 'timeupdate', sync, { signal }); - listen(target, 'durationchange', sync, { signal }); - listen(target, 'seeked', sync, { signal }); - listen(target, 'loadedmetadata', sync, { signal }); - listen(target, 'emptied', sync, { signal }); + listen(target, 'timeupdate', update, { signal }); + listen(target, 'durationchange', update, { signal }); + listen(target, 'seeked', update, { signal }); + listen(target, 'loadedmetadata', update, { signal }); + listen(target, 'emptied', update, { signal }); }, request: { diff --git a/packages/core/src/dom/store/slices/volume.ts b/packages/core/src/dom/store/slices/volume.ts index 6ea4e01b..5e4a5232 100644 --- a/packages/core/src/dom/store/slices/volume.ts +++ b/packages/core/src/dom/store/slices/volume.ts @@ -22,8 +22,7 @@ export const volumeSlice = createSlice()({ }), subscribe: ({ target, update, signal }) => { - const sync = () => update(); - listen(target, 'volumechange', sync, { signal }); + listen(target, 'volumechange', update, { signal }); }, request: { diff --git a/packages/store/README.md b/packages/store/README.md index 71d0f642..f685cff9 100644 --- a/packages/store/README.md +++ b/packages/store/README.md @@ -63,7 +63,7 @@ const audioSlice = createSlice()({ }), subscribe: ({ target, update, signal }) => { - target.addEventListener('volumechange', () => update(), { signal }); + target.addEventListener('volumechange', update, { signal }); }, request: { @@ -263,44 +263,37 @@ const unsubscribe = store.subscribe((state) => { // Single value - only fires when volume changes store.subscribe( - (s) => s.volume, - (volume) => console.log('Volume:', volume) + s => s.volume, + volume => console.log('Volume:', volume) ); // Multiple values - auto-optimized with key-based subscription store.subscribe( - (s) => ({ volume: s.volume, muted: s.muted }), + s => ({ volume: s.volume, muted: s.muted }), ({ volume, muted }) => updateAudioUI(volume, muted) ); // Derived value store.subscribe( - (s) => Math.round(s.volume * 100), - (percent) => console.log(`${percent}%`) + s => Math.round(s.volume * 100), + percent => console.log(`${percent}%`) ); // Custom equality function store.subscribe( - (s) => s.playlist, - (playlist) => renderPlaylist(playlist), + s => s.playlist, + playlist => renderPlaylist(playlist), { equalityFn: shallowEqual } ); ``` -Slices can push partial updates to avoid full syncs: +Slices sync state from the target via `getSnapshot`. The `update` callback triggers a sync, and the store only notifies subscribers for keys that actually changed: ```ts subscribe: ({ target, update, signal }) => { - // Partial - only update currentTime - target.addEventListener( - 'timeupdate', - () => { - update({ currentTime: target.currentTime }); - }, - { signal } - ); - - // Full sync + // Each event triggers a full sync via getSnapshot + // Only changed keys notify their subscribers + target.addEventListener('timeupdate', update, { signal }); target.addEventListener('durationchange', update, { signal }); }; ``` @@ -542,7 +535,7 @@ const store = createStore({ ], queue: createQueue({ // Default scheduler for requests without schedule - scheduler: (flush) => queueMicrotask(flush), + scheduler: flush => queueMicrotask(flush), // Lifecycle hooks onDispatch: (request) => { @@ -628,7 +621,7 @@ const store = createStore({ slices: [ /* ... */ ], - state: (initial) => new VueStateAdapter(initial), + state: initial => new VueStateAdapter(initial), }); ``` diff --git a/packages/store/src/core/slice.ts b/packages/store/src/core/slice.ts index fe91420c..470b0c19 100644 --- a/packages/store/src/core/slice.ts +++ b/packages/store/src/core/slice.ts @@ -35,16 +35,14 @@ export interface SliceGetSnapshotContext { export type SliceSubscribe = (ctx: SliceSubscribeContext) => void; -export interface SliceSubscribeContext { +export interface SliceSubscribeContext { target: Target; - update: SliceUpdate; + update: SliceUpdate; signal: AbortSignal; } -export interface SliceUpdate { - (): void; - (state: Partial): void; -} +/** Sync slice state from target via getSnapshot. */ +export type SliceUpdate = () => void; export interface SliceConfig< Target, diff --git a/packages/store/src/core/store.ts b/packages/store/src/core/store.ts index e43c1d55..6f44eeb8 100644 --- a/packages/store/src/core/store.ts +++ b/packages/store/src/core/store.ts @@ -1,6 +1,13 @@ import type { PendingTask, Task, TaskContext } from './queue'; import type { RequestMeta, RequestMetaInit, ResolvedRequestConfig } from './request'; -import type { AnySlice, Slice, UnionSliceRequests, UnionSliceState, UnionSliceTarget, UnionSliceTasks } from './slice'; +import type { + AnySlice, + SliceUpdate, + UnionSliceRequests, + UnionSliceState, + UnionSliceTarget, + UnionSliceTasks, +} from './slice'; import type { StateFactory } from './state'; import { getSelectorKeys } from '@videojs/utils/object'; @@ -115,20 +122,10 @@ export class Store[] = AnySlice[ return () => this.#detach(); } - #createUpdate(slice: Slice) { - return (partial?: Partial) => { + #createUpdate(slice: AnySlice): SliceUpdate { + return () => { const target = this.#target; - if (!target) return; - - try { - if (partial === undefined) { - this.#syncSlice(slice, target); - } else { - this.#state.patch(partial as Partial>); - } - } catch (error) { - this.#handleError({ error }); - } + if (target) this.#syncSlice(slice, target); }; } diff --git a/packages/store/src/core/tests/integration/store.test.ts b/packages/store/src/core/tests/integration/store.test.ts index c3949589..96abc9a1 100644 --- a/packages/store/src/core/tests/integration/store.test.ts +++ b/packages/store/src/core/tests/integration/store.test.ts @@ -15,7 +15,7 @@ describe('store lifecycle integration', () => { getSnapshot: ({ target }) => ({ count: target.value }), subscribe: ({ target, update, signal }) => { events.push('subscribe'); - target.addEventListener('change', () => update(), { signal }); + target.addEventListener('change', update, { signal }); signal.addEventListener('abort', () => events.push('unsubscribe')); }, request: { @@ -231,7 +231,7 @@ describe('request coordination', () => { }); describe('state syncing', () => { - it('partial updates only trigger relevant subscriptions', async () => { + it('updates only trigger subscriptions for changed keys', async () => { const volumeUpdates: number[] = []; const mutedUpdates: boolean[] = []; @@ -250,21 +250,8 @@ describe('state syncing', () => { muted: target.muted, }), subscribe: ({ target, update, signal }) => { - target.addEventListener( - 'volumechange', - () => { - update({ volume: target.volume }); - }, - { signal }, - ); - - target.addEventListener( - 'mutechange', - () => { - update({ muted: target.muted }); - }, - { signal }, - ); + target.addEventListener('volumechange', update, { signal }); + target.addEventListener('mutechange', update, { signal }); }, request: { setVolume: (volume: number, { target }) => { diff --git a/packages/store/src/core/tests/store.test.ts b/packages/store/src/core/tests/store.test.ts index f8e23f7b..7d3fb8d8 100644 --- a/packages/store/src/core/tests/store.test.ts +++ b/packages/store/src/core/tests/store.test.ts @@ -21,10 +21,9 @@ describe('store', () => { muted: target.muted, }), subscribe: ({ target, update, signal }) => { - const handler = () => update(); - target.addEventListener('volumechange', handler); + target.addEventListener('volumechange', update); signal.addEventListener('abort', () => { - target.removeEventListener('volumechange', handler); + target.removeEventListener('volumechange', update); }); }, request: { diff --git a/packages/store/src/react/tests/create-store.test.tsx b/packages/store/src/react/tests/create-store.test.tsx index 634b4b55..2a7fa18b 100644 --- a/packages/store/src/react/tests/create-store.test.tsx +++ b/packages/store/src/react/tests/create-store.test.tsx @@ -21,10 +21,9 @@ describe('createStore', () => { muted: target.muted, }), subscribe: ({ target, update, signal }) => { - const handler = () => update(); - target.addEventListener('volumechange', handler); + target.addEventListener('volumechange', update); signal.addEventListener('abort', () => { - target.removeEventListener('volumechange', handler); + target.removeEventListener('volumechange', update); }); }, request: { diff --git a/packages/store/src/react/tests/hooks.test.tsx b/packages/store/src/react/tests/hooks.test.tsx index a74e38b3..fe5964d9 100644 --- a/packages/store/src/react/tests/hooks.test.tsx +++ b/packages/store/src/react/tests/hooks.test.tsx @@ -20,10 +20,9 @@ describe('react hooks', () => { muted: target.muted, }), subscribe: ({ target, update, signal }) => { - const handler = () => update(); - target.addEventListener('volumechange', handler); + target.addEventListener('volumechange', update); signal.addEventListener('abort', () => { - target.removeEventListener('volumechange', handler); + target.removeEventListener('volumechange', update); }); }, request: {