← All reusables
withInstance
extensionInstallation
npx jsrepo add github/reatom/reusables withInstance Registry dependencies
Copy the source code below and save it to the specified file path in your project.
with-instance.ts
import { type AtomLike, type Ext } from '@reatom/core'
import { reatomInstance } from './reatom-instance'
type InstanceExt<I> = {
instance: ReturnType<typeof reatomInstance<I>>
}
/**
* Atom extension that adds an `.instance` property — a lifecycle-managed
* instance derived from the atom's value.
*
* When the source atom changes, the previous instance is disposed and a new one
* is created automatically.
*
* @example
* const dimensions = atom({ x: 1, y: 1, z: 1 }).extend(
* withInstance(
* (dims) => new BoxGeometry(dims().x, dims().y, dims().z),
* (geometry) => geometry.dispose(),
* ),
* )
* // Access via dimensions.instance()
*
* @param create - Factory receiving the source atom
* @param dispose - Cleanup function called when instance changes
*/
export const withInstance =
<T extends AtomLike, I>(
create: (target: T) => I,
dispose?: (instance: I) => void,
): Ext<T, InstanceExt<I>> =>
(target) => ({ instance: reatomInstance(() => create(target), dispose) }) Documentation
withInstance
Atom extension that adds a lifecycle-managed .instance property derived from the atom's value.
Depends on reatomInstance.
withInstance(create, dispose?)
When the source atom changes, the previous instance is disposed and a new one is created automatically.
Parameters
| Parameter | Type | Description |
|---|---|---|
create |
(target: T) => I |
Factory receiving the source atom |
dispose |
(instance: I) => void |
Cleanup callback (optional) |
Example
import { atom } from '@reatom/core'
import { withInstance } from '#reatom/extension/with-instance'
const dimensions = atom({ x: 1, y: 1, z: 1 }).extend(
withInstance(
(dims) => new BoxGeometry(dims().x, dims().y, dims().z),
(geometry) => geometry.dispose(),
),
)
// Access the managed instance via .instance
const unsub = dimensions.instance.subscribe((geometry) => {
scene.add(new Mesh(geometry, material))
})
// When dimensions change, old geometry is disposed,
// new one is created automatically.
dimensions.set({ x: 2, y: 3, z: 4 })
Composing instances
Instances created with withInstance can be consumed by other reatomInstance calls, forming reactive dependency chains:
import { reatomInstance } from '#reatom/factory/reatom-instance'
const material = atom({ color: '#00ff00' }).extend(
withInstance(
(params) => new MeshStandardMaterial({ color: params().color }),
(mat) => mat.dispose(),
),
)
const mesh = reatomInstance(
() => new Mesh(dimensions.instance(), material.instance()),
(m) => m.removeFromParent(),
)
Example
import { atom, effect, withComputed } from '@reatom/core'
import { reatomInstance } from './reatom-instance'
import { withInstance } from './with-instance'
// --- Derive an instance from an atom ---
const config = atom({ width: 800, height: 600 }, 'config').extend(
withInstance(
(target) => {
const { width, height } = target()
const el = document.createElement('canvas')
el.width = width
el.height = height
return el
},
(el) => el.remove(),
),
)
// Access the managed canvas via config.instance
effect(() => {
document.body.appendChild(config.instance())
})
// --- Composing instances ---
const resolution = atom({ w: 1920, h: 1080 }, 'resolution').extend(
withInstance(
(res) => {
const c = document.createElement('canvas')
c.width = res().w
c.height = res().h
return c
},
(c) => c.remove(),
),
)
const renderer = reatomInstance(
() => resolution.instance().getContext('2d')!,
).extend(
withComputed((ctx) => {
ctx.fillStyle = '#333'
ctx.fillRect(
0,
0,
resolution.instance().width,
resolution.instance().height,
)
return ctx
}),
)
// Subscribe to activate the chain
void renderer.subscribe(() => {})