[]previous and next page

Reactive values

Because the provider reads value once, value={count()} freezes the number at its first value.

For agentsUpdated

Because the provider reads value once, value={count()} freezes the number at its first value. Provide a function (an accessor) or a store, and read it where you use it.

src/counter-context.tsxtsx
import { createContext, useContext, useStore } from "@shadow-js/core";
import type { Accessor } from "@shadow-js/core";

export interface CounterApi {
  count: Accessor<number>;
  increment: () => void;
}

export const CounterContext = createContext<CounterApi>();

export function useCounter(): CounterApi {
  return useContext(CounterContext);
}

export function CounterProvider(props: { children?: unknown }) {
  const [count, setCount] = useStore(0);
  const api: CounterApi = { count, increment: () => setCount((n) => n + 1) };
  return <CounterContext.Provider value={api}>{props.children}</CounterContext.Provider>;
}

function Display() {
  const counter = useCounter();
  return <p>count {counter.count()}</p>;
}

export function Page() {
  return (
    <CounterProvider>
      <Display />
    </CounterProvider>
  );
}

A provided store keeps the same identity and is already fine-grained: value={{ state, setState }} where [state, setState] = useStore(...) needs no accessor, because consumers read state.x where they use it.

src/cart-context.tsxtsx
import { createContext, type Store, useContext, useStore } from "@shadow-js/core";
import type { ParentProps } from "@shadow-js/web";

interface CartState {
  items: string[];
}

interface CartApi {
  cart: Store<CartState>;
  add: (item: string) => void;
}

const CartContext = createContext<CartApi>(undefined, { name: "Cart" });

export function CartProvider(props: ParentProps) {
  const [cart, setCart] = useStore<CartState>({ items: [] });
  const api: CartApi = {
    cart,
    add: (item) => setCart((draft) => void draft.items.push(item)),
  };
  return <CartContext.Provider value={api}>{props.children}</CartContext.Provider>;
}

export function useCart(): CartApi {
  return useContext(CartContext);
}

function Summary() {
  const { cart } = useCart();
  return <p>Cart: {cart.items.length} items</p>;
}

export function Page() {
  return (
    <CartProvider>
      <Summary />
    </CartProvider>
  );
}