Context carries values through the reactive scope so deeply nested components can read them without prop drilling. A single function acts as both the provider and the reader.
context(defaultValue?) returns a useContext function. Call it with
no argument to read the current value (or the default if nothing was
provided). Call it with a value and a fn to push that value for the
duration of fn — children rendered inside read the new value;
everything outside keeps the previous one. The new value replaces
the inherited one entirely; to inherit-and-override, build the merged
object yourself before providing it.
A Provider JSX component is attached for use in the tree, plus a
walk helper that climbs the parent chain of a context value.
| name | type | description |
|---|---|---|
defaultValue |
T |
value returned by reads when no provider is in scope. Optional. |
Returns: useContext(newValue?, fn?) — a function that reads when
called bare and provides for the duration of fn when called with
(newValue, fn), returning fn's result. It carries:
useContext.Provider — a JSX component; <Ctx.Provider value={…}>
pushes value to its children.useContext.walk(callback, context?) — walks the parent property
of the context value, calling callback at each level; return
true from callback to stop.// create (defaultValue is optional)
const useCtx = context('default value')
// read
useCtx()
// provide, scoped to fn
useCtx('new value', fn)
context(defaultValue) returns a useContext function with a
Provider JSX component attached. Wrap a subtree in
<Theme.Provider value={…}/> and any descendant call to Theme()
returns that value; with no provider, Theme() returns the default.
Providers nest — inner ones override outer ones for their subtree.
import { context, render } from 'pota'
const Theme = context('light')
function Label() {
return <p>theme is {Theme()}</p>
}
function App() {
return (
<div>
<Label />
<Theme.Provider value="dark">
<Label />
<Theme.Provider value="contrast">
<Label />
</Theme.Provider>
</Theme.Provider>
</div>
)
}
render(App)
Calling the context function as Theme(newValue, fn) runs fn with
the override applied and returns its result; outside fn, the
previous value is restored. Useful when you need the context override
outside the JSX tree — computing a derived value, inside an
effect, and so on.
import { context, render, signal } from 'pota'
const Theme = context('light')
const log = signal('')
log.write(`Theme(): ${Theme()}`) // 'light'
const result = Theme('dark', () => {
return Theme('contrast', () => Theme())
})
log.update(s => s + `\nresult: ${String(result)}`) // 'contrast'
log.update(s => s + `\nTheme(): ${Theme()}`) // 'light' — restored
function App() {
return <p>{log.read}</p>
}
render(App)
Putting a signal into the context value gives descendants
both a reactive read and a write channel. The provider owns the signal
once; any nested component swaps the theme by calling the context's
set channel. Note the reader is passed as theme.read (the reader
function) so the read stays reactive.
import { context, render, signal } from 'pota'
type ThemeContext = {
value: () => string
set: (next: string) => void
}
const Theme = context<ThemeContext>({
value: () => 'light',
set: next => {},
})
function Toolbar() {
const t = Theme()
return (
<div>
<p>current: {t.value}</p>
<button on:click={() => t.set('light')}>light</button>
<button on:click={() => t.set('dark')}>dark</button>
</div>
)
}
function App() {
const theme = signal('light')
return (
<Theme.Provider
value={{
value: theme.read,
set: v => theme.write(v),
}}
>
<Toolbar />
</Theme.Provider>
)
}
render(App)