[]previous and next page

Redux and Zustand

State that lives outside components is a module-level useStore.

For agentsUpdated

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;
}