Redux and Zustand
State that lives outside components is a module-level useStore.
State that lives outside components is a module-level useStore. Reading a property inside an effect or JSX is
the subscription, so there is no useSelector, no equality function and no provider. Actions are plain
functions that call the setter. A reducer is a function over a draft (like Immer, which Redux Toolkit uses).
src/bears.tsts
import { useStore } from "@shadow-js/core";
export const [bears, setBears] = useStore({ count: 0, honey: 10 });
export const addBear = (): void => {
setBears((draft) => {
draft.count += 1;
draft.honey -= 1;
});
};
useReducer and useSyncExternalStore are built in (useReducer, useExternalStore). The example keeps local copies of
their shape so it runs on its own; the built-ins have the same arguments:
src/hooks.tsts
import { type Accessor, onMount, useSignal } from "@shadow-js/core";
export function useReducer<S, A>(
reducer: (state: S, action: A) => S,
initial: S,
): [Accessor<S>, (action: A) => void] {
// useSignal: S is generic, and a plain-object state must stay one cell (useStore would make a store).
const [state, setState] = useSignal(initial);
return [state, (action) => setState((current) => reducer(current, action))];
}
export function useSyncExternalStore<T>(
subscribe: (notify: () => void) => () => void,
getSnapshot: () => T,
): Accessor<T> {
// useSignal: T is generic, and a plain-object snapshot must stay one cell (useStore would make a store).
const [snapshot, setSnapshot] = useSignal(getSnapshot());
onMount(() => {
const read = (): void => {
setSnapshot(() => getSnapshot());
};
read();
return subscribe(read);
});
return snapshot;
}