feat(store): useOptimistic hook for react (#291)

This commit is contained in:
rahim
2026-01-07 00:23:23 +11:00
committed by GitHub
parent ecd4940bc7
commit 8da3ab1b35
6 changed files with 535 additions and 10 deletions
@@ -1,4 +1,5 @@
import { noop } from '@videojs/utils/function';
import { createSlice } from '../../../core/slice';
import { createStore as createCoreStore } from '../../../core/store';
@@ -70,12 +71,26 @@ export const asyncAudioSlice = createSlice<AsyncMockMedia>()({
return volume;
},
},
slowSetVolume: {
handler: async (volume: number, { target }) => {
await new Promise(resolve => setTimeout(resolve, 50));
target.volume = volume;
target.dispatchEvent(new Event('volumechange'));
return volume;
},
},
failingRequest: {
handler: async () => {
await Promise.resolve();
throw new Error('Request failed');
},
},
failingSetVolume: {
handler: async () => {
await new Promise(resolve => setTimeout(resolve, 10));
throw new Error('Test error');
},
},
},
});
@@ -90,3 +105,53 @@ export function createAsyncTestStore() {
return { store, target };
}
/** Slice with custom keys (name !== key) for testing superseding behavior. */
export const customKeySlice = createSlice<MockMedia>()({
initialState: { volume: 1, muted: false },
getSnapshot: ({ target }) => ({
volume: target.volume,
muted: target.muted,
}),
subscribe: ({ target, update, signal }) => {
const handler = () => update();
target.addEventListener('volumechange', handler);
signal.addEventListener('abort', () => {
target.removeEventListener('volumechange', handler);
});
},
request: {
// name='adjustVolume', key='audio-settings'
adjustVolume: {
key: 'audio-settings',
handler: async (volume: number, { target }): Promise<number> => {
await new Promise(resolve => setTimeout(resolve, 20));
target.volume = volume;
target.dispatchEvent(new Event('volumechange'));
return volume;
},
},
// name='toggleMute', key='audio-settings' (same key - will supersede adjustVolume)
toggleMute: {
key: 'audio-settings',
handler: async (muted: boolean, { target }): Promise<boolean> => {
await new Promise(resolve => setTimeout(resolve, 20));
target.muted = muted;
target.dispatchEvent(new Event('volumechange'));
return muted;
},
},
},
});
export function createCustomKeyTestStore() {
const store = createCoreStore({
slices: [customKeySlice],
onError: noop,
});
const target = new MockMedia();
store.attach(target);
return { store, target };
}