drainUnbind
Flush the pending unbind queue after nodes have been removed.
drainUnbindCleanup and Unbind
Flush the pending unbind queue after nodes have been removed.
Overview
drainUnbind flushes Regor's pending unbind queue immediately.
Regor defers some unbind work after removeNode(...) for better runtime behavior. Use drainUnbind() when you need deterministic cleanup timing.
Try it live
Own a node through its full lifetimeLive API
Source value: 0 · Node: absent
Source observers
Root unbinders
Custom cleanup calls
Unbind keeps the node but freezes its bindings. Remove + drain removes it and flushes deferred cleanup.
import {
addUnbinder,
createApp,
drainUnbind,
getBindData,
html,
observerCount,
onUnmounted,
ref,
removeNode,
sref,
unbind,
useScope,
} from 'regor'
export function createCleanup(interactive = false) {
const host = sref<HTMLElement | null>(null)
const source = ref(0),
value = ref(0),
present = ref(false),
bound = ref(false)
const observers = ref(0),
callbacks = ref(0),
registered = ref(0)
let node: HTMLElement | null = null
const inspect = () => {
observers(observerCount(source))
registered(node ? getBindData(node).unbinders.length : 0)
}
const mount = () => {
if (!host() || node) return
node = document.createElement('div')
node.className = 'directive-card api-owned-node'
host()!.appendChild(node)
createApp(
useScope(() => ({ source })),
{
element: node,
template:
'<strong>Owned node</strong><p>Bound value: <output r-text="source"></output></p>',
},
)
addUnbinder(node, () => callbacks(callbacks() + 1))
present(true)
bound(true)
inspect()
}
const detach = () => {
if (node) unbind(node)
bound(false)
inspect()
}
const remove = async () => {
if (!node) return
removeNode(node)
await drainUnbind()
node = null
present(false)
bound(false)
inspect()
}
onUnmounted(() => {
if (node) {
unbind(node)
node.remove()
}
})
return {
interactive,
host,
value,
present,
bound,
observers,
callbacks,
registered,
mount,
detach,
remove,
increment: () => {
source(source() + 1)
value(source())
inspect()
},
}
}
export const cleanupTemplate = html` <div class="guide-demo">
<div class="guide-session-bar">
<button type="button" @click="mount" :disabled="!interactive || present">
Mount node</button
><button type="button" @click="increment" :disabled="!interactive">
Write +1</button
><button type="button" @click="detach" :disabled="!interactive || !bound">
Unbind</button
><button type="button" @click="remove" :disabled="!interactive || !present">
Remove + drain
</button>
</div>
<p>
Source value: <strong>{{ value }}</strong> · Node: {{ present ? 'present' :
'absent' }}
</p>
<div :ref="host"></div>
<div class="guide-metrics">
<div><output>{{ observers }}</output><span>Source observers</span></div>
<div><output>{{ registered }}</output><span>Root unbinders</span></div>
<div><output>{{ callbacks }}</output><span>Custom cleanup calls</span></div>
</div>
<p class="guide-hint">
Unbind keeps the node but freezes its bindings. Remove + drain removes it
and flushes deferred cleanup.
</p>
</div>` import { createApp, RegorConfig, useScope } from 'regor'
import { apiExamples } from './examples'
const element = document.getElementById('api-demo')
const name = element?.dataset.example
if (element && name && Object.hasOwn(apiExamples, name)) {
const example = apiExamples[name as keyof typeof apiExamples]
const scope = useScope<object>(() => example.create(true))
const config =
'config' in scope.context && scope.context.config instanceof RegorConfig
? scope.context.config
: undefined
createApp(scope, { element, template: example.template }, config)
} Mount, write, unbind, or remove and drain. Watch the DOM, observer count, and registered cleanup.
Usage
import { createApp, drainUnbind, html, ref } from 'regor'
const root = document.createElement('div')
const show = ref(true)
const app = createApp({
template: html`<div><span r-if="show">Hello</span></div>`,
setup: { show },
})
app.mount(root)
show(false) // schedules node removal + deferred unbind
await drainUnbind() // force pending unbind cleanup now Signature
drainUnbind(): Promise<void> Use it when cleanup must be complete
Await drainUnbind in checks or integrations that depend on the deferred queue being empty.
Notes
- Safe to call when there is nothing pending.
- Useful in tests when you need cleanup completed before assertions.
- Complements
removeNodeandunbind; it does not replace app lifecycle (app.unmount()).