Lifecycle

onCreate

onCreate runs once after the component mounts and after the first render.

Micra.define('users', {
  state: { users: [], loading: true },

  async onCreate() {
    this.state.users = await this.fetch('/api/users') as any[]
    this.state.loading = false
  },
})

Key points:

For the full loading / error / data lifecycle around a fetch — with cancellation and a refetch() — wrap it in the resource() recipe instead of writing the states by hand.

onDestroy

onDestroy runs when this.destroy() is called.

Micra.define('clock', {
  state: { now: Date.now() },

  onCreate() {
    this.timer = window.setInterval(() => {
      this.state.now = Date.now()
    }, 1000)
  },

  onDestroy() {
    clearInterval(this.timer)
  },
})

Use it to clean up timers, manual DOM listeners, third-party widgets, and other external resources.

this.on() auto-cleanup

Subscriptions created with this.on() are removed automatically on destroy.

onCreate() {
  this.on('modal:open', payload => {
    console.log(payload)
  })
}

You do not need to call the returned unsubscribe function unless you want to stop listening earlier.

Manual destroy

Every instance has destroy().

const modal = Micra.mount('#modal', definition)
modal?.destroy()

destroy():

It does not remove the root DOM element.

Re-mount behavior

Mounting the same root twice returns the existing instance.

const a = Micra.mount('#counter', definition)
const b = Micra.mount('#counter', definition)

console.log(a === b) // true

This also makes Micra.start() safe to call multiple times. Already-mounted roots are skipped.

If you call destroy(), you can mount that root again and get a new instance.