If a component class fits your use case, extend Pota and render the
class as you would any component. Define a render(props) method and
instances are created automatically when JSX uses the class.
If the subclass defines a ready() or cleanup() method, pota calls
them at the matching lifecycle points — you don't need to register
them manually. The instance has this.props available; signals stored
as instance fields work the same as in function components.
Signals live as instance fields, ready()/cleanup() are the mount
and dispose hooks, and render(props) returns the JSX.
import { Pota, render, signal } from 'pota'
import { Show } from 'pota/components'
const status = signal('not mounted')
class Counter extends Pota {
count = signal(0)
ready() {
status.write('mounted')
}
cleanup() {
status.write('unmounted')
}
render(props) {
return (
<button on:click={() => this.count.update(n => n + 1)}>
{props.label}: {this.count.read}
</button>
)
}
}
function App() {
const show = signal(true)
return (
<div>
<button on:click={() => show.update(v => !v)}>
mount / unmount
</button>
<Show when={show.read}>
<Counter label="clicks" />
</Show>
<p>status: {status.read}</p>
</div>
)
}
render(App)
A props instance field acts as defaults; props passed in JSX merge
on top of them, and this.props inside render() is the frozen,
merged result. The handle returned by render disposes the tree,
firing cleanup().
import { Pota, render, signal } from 'pota'
const log = signal('ready')
class MyComponent extends Pota {
props = {
some: 'default',
children: 'quack',
}
ready() {
log.write('ready callback!')
}
cleanup() {
log.write('cleanup callback!')
}
render() {
return <main>{this.props.children}</main>
}
}
const dispose = render(
<MyComponent some="lala">hello from class!</MyComponent>,
)
// the log lives outside the disposed tree, so it survives to
// show the cleanup message
render(
<div>
<button
name="button"
on:click={dispose}
>
dispose
</button>
<p>{log.read}</p>
</div>,
)