Exact semantics
Read the value in the component body (or in an effect) and keep it in a variable; do not call useContext inside handlers:
- The value is read once. The provider evaluates
props.valuewhen it is created. Pass an accessor or a store to provide something that changes (see Reactive values). - Children stay reactive. The provider returns an accessor of its children, so a children expression that reads signals still updates, and everything created inside sees the provided value.
- Lookup follows owners.
useContextreads the current owner, so it works while a component runs, inside effects and memos created there, and insiderunWithOwner. After anawait, in a timer, or in an event handler there is no owner, and the lookup falls back to the default value or throws. - Nearest wins. An inner provider shadows an outer one for its own subtree only.
- No provider, no default: throws.
useContextthrows aShadowErrorwith codeCONTEXT_MISSING. A context created withcreateContext()and no default value therefore means "a provider is required". - A default of
undefinedcounts as no default. To make a context optional, give it a default that is notundefined, such asnull. - Providers dispose with their owner. The provider creates a child owner; effects and cleanups created below it are disposed with the provider's parent.
- On the server the provider builds its subtree once; nothing is reactive there.
Read the value in the component body (or in an effect) and keep it in a variable; do not call useContext inside handlers:
src/theme-button.tsxtsx
import { useContext } from "@shadow-js/core";
import { ThemeContext } from "./theme";
export function ThemeButton() {
const theme = useContext(ThemeContext); // during setup
return (
<button type="button" onClick={() => console.log(theme)}>
{theme}
</button>
);
}
export function Page() {
return (
<ThemeContext.Provider value="dark">
<ThemeButton />
</ThemeContext.Provider>
);
}