Creates a new top-level owner scope. The callback receives a dispose
function that tears down everything created inside. Reactive work that
outlives a component (long-lived subscriptions, imperative rendering,
stores instantiated at module load) belongs in a root.
render creates one for you internally; call root directly
when there's no render tree.
| name | type | description |
|---|---|---|
fn |
(dispose) => T |
runs once, immediately. Receives the disposer for the root. |
options? |
EffectOptions |
optional owner options assigned onto the root. |
Returns: the return value of fn.
root runs fn in a new owner that is not attached to any parent —
cleanup registrations stick around until you call the
disposer fn received. Use it for reactive code outside a component
tree (workers, modal managers, background timers).
import { cleanup, effect, root, signal } from 'pota'
const dispose = root(dispose => {
const ticks = signal(0)
effect(() => {
document.title = `ticks: ${ticks.read()}`
})
const id = setInterval(() => ticks.update(n => n + 1), 1000)
cleanup(() => clearInterval(id))
return dispose
})
// later, e.g. on hot-reload or page teardown:
// dispose()
Wrapping a module's reactive setup in root gives those primitives
their own owner — they don't get cleaned up when a component unmounts,
and any cleanups they register live for the whole page.
import { effect, root, signal } from 'pota'
export const session = root(() => {
const user = signal(null)
effect(() => {
if (user.read()) {
document.documentElement.classList.add('logged-in')
} else {
document.documentElement.classList.remove('logged-in')
}
})
return {
user: user.read,
login: u => user.write(u),
logout: () => user.write(null),
}
})