Registers a callback that runs when the current tracking scope is disposed — typically a parent component unmount, render's returned disposer being called, or a re-run memo recreating its scope. Pair it with anything you set up imperatively (timers, listeners, third-party instances) so it doesn't outlive the scope.
Callbacks run in reverse registration order (LIFO), and child scopes
dispose before their parents. Don't rely on the scope's DOM having
been detached yet inside a cleanup — in most scopes the nodes leave
the document at the end of disposal, after the cleanups (keyed
<For> rows are the exception: their nodes detach
first). cleanup returns the same fn it was given.
| name | type | description |
|---|---|---|
fn |
() => void |
function to run once the tracking scope is disposed |
Returns: the same fn.
A setInterval lives outside the reactive graph, so it must be torn
down by hand. Registering clearInterval with cleanup ties the
timer's lifetime to the component's.
import { cleanup, render, signal } from 'pota'
function Clock() {
const now = signal(new Date())
const id = setInterval(() => now.write(new Date()), 1000)
cleanup(() => clearInterval(id))
return <p>{() => now.read().toLocaleTimeString()}</p>
}
render(Clock)
Anything attached imperatively to window or document outlives the
component unless you remove it. cleanup runs when the owner
disposes, so the listener goes away with the component.
import { cleanup, render, signal } from 'pota'
function Keys() {
const last = signal('')
const onKey = e => last.write(e.key)
window.addEventListener('keydown', onKey)
cleanup(() => window.removeEventListener('keydown', onKey))
return <p>last key: {last.read}</p>
}
render(Keys)
Child scopes dispose before their parents — the deepest cleanups fire
first. Unmount the tree and the cleanup: lines appear from the
inside out: Child before Parent.
import { cleanup, render, signal } from 'pota'
import { Show } from 'pota/components'
const log = signal('mounted — unmount to see disposal order')
function Child() {
cleanup(() => log.update(s => s + 'cleanup: Child\n'))
return <p>child</p>
}
function Parent() {
cleanup(() => log.update(s => s + 'cleanup: Parent'))
return (
<main>
<Child />
</main>
)
}
function App() {
const show = signal(true)
return (
<div>
<button
on:click={() => {
log.write('')
show.update(v => !v)
}}
>
mount / unmount
</button>
<Show when={show.read}>
<Parent />
</Show>
<pre>{log.read}</pre>
</div>
)
}
render(App)