refactor(store): simplify state management + computeds (#321)

This commit is contained in:
rahim
2026-01-22 21:56:47 +11:00
committed by GitHub
parent 78fab92a9d
commit e01ce5bc1c
23 changed files with 822 additions and 811 deletions
+11 -19
View File
@@ -1,32 +1,24 @@
import type { Reactive } from '../../core/state';
import type { State } from '../../core/state';
import { useState, useSyncExternalStore } from 'react';
import { track } from '../../core/state';
import { useSyncExternalStore } from 'react';
/**
* Subscribe to reactive state and re-render when accessed properties change.
*
* Automatically tracks which properties are accessed during render and only
* re-renders when those specific properties change.
*
* @param state - Reactive state created by `reactive()`
* @returns The state, which triggers re-renders when accessed properties change
* Subscribe to state and re-render when state changes.
*
* @example
* ```tsx
* function VolumeDisplay() {
* const state = useSnapshot(store.state);
* return <span>{Math.round(state.volume * 100)}%</span>;
* const { volume } = useSnapshot(store.state);
* return <span>{Math.round(volume * 100)}%</span>;
* }
* ```
*/
export function useSnapshot<T extends object>(state: Reactive<T>): T {
const [{ tracked, subscribe, getSnapshot, next }] = useState(() => track(state));
useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
next();
return tracked;
export function useSnapshot<T extends object>(state: State<T>): T {
return useSyncExternalStore(
onStoreChange => state.subscribe(onStoreChange),
() => state.current,
() => state.current,
);
}
export namespace useSnapshot {
+3 -24
View File
@@ -1,9 +1,7 @@
import type { TasksRecord } from '../../core/queue';
import type { AnyStore, InferStoreTasks } from '../../core/store';
import { useCallback, useRef, useSyncExternalStore } from 'react';
import { subscribe } from '../../core/state';
import { useSnapshot } from './use-snapshot';
/**
* Subscribe to task queue state.
@@ -11,9 +9,6 @@ import { subscribe } from '../../core/state';
* Returns a record of all tasks keyed by request name.
* Re-renders when any task is added, updated, or removed.
*
* @param store - The store instance to subscribe to
* @returns Record of tasks keyed by request name
*
* @example
* ```tsx
* function TaskList() {
@@ -31,22 +26,6 @@ import { subscribe } from '../../core/state';
* }
* ```
*/
export function useTasks<S extends AnyStore>(store: S): TasksRecord<InferStoreTasks<S>> {
const versionRef = useRef(0);
const subscribeToQueue = useCallback(
(onStoreChange: () => void) =>
subscribe(store.queue.tasks, () => {
versionRef.current++;
onStoreChange();
}),
[store],
);
const getSnapshot = useCallback(() => versionRef.current, []);
useSyncExternalStore(subscribeToQueue, getSnapshot, getSnapshot);
// Return the tasks proxy directly
return store.queue.tasks as TasksRecord<InferStoreTasks<S>>;
export function useTasks<Store extends AnyStore>(store: Store): TasksRecord<InferStoreTasks<Store>> {
return useSnapshot(store.queue.tasks) as TasksRecord<InferStoreTasks<Store>>;
}
@@ -42,7 +42,7 @@ describe('createStore', () => {
const store = create();
expect(store).toBeDefined();
expect(store.state).toEqual({ volume: 1, muted: false });
expect(store.state.current).toEqual({ volume: 1, muted: false });
});
});
@@ -55,7 +55,7 @@ describe('createStore', () => {
});
expect(result.current).toBeDefined();
expect(result.current.state).toEqual({ volume: 1, muted: false });
expect(result.current.state.current).toEqual({ volume: 1, muted: false });
});
it('destroys store on unmount', () => {