Reactive values
Because the provider reads value once, value={count()} freezes the number at its first value.
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>
);
}