Stateful Components

Stateful Components

Use StatefulComponent when your component needs to manage internal state. The component initializes state from props, then re-renders whenever state changes.

Live counter

This is a real Preactile component rendered live in your browser. Click the buttons:

import preactile.*

object Counter extends StatefulComponent[Unit, Int]:
  override def initialState(props: Unit): Int = 0

  def render(props: Unit, state: Int, instance: Instance): VNode =
    E.div(
      A.`class`("counter-demo"),
      E.button(A.onClick(_ => instance.setState(state - 1)), "-"),
      E.span(" ", E.strong(state.toString), " "),
      E.button(A.onClick(_ => instance.setState(state + 1)), "+"),
    )
end Counter

This example runs in your browser; enable JavaScript to see it.

Key points:

State derived from props

Initial state can depend on incoming props:

import preactile.*

case class TimerProps(initialDelayMs: Int)

object Timer extends StatefulComponent[TimerProps, Int]:
  override def initialState(props: TimerProps): Int = props.initialDelayMs

  def render(props: TimerProps, state: Int, instance: Instance): VNode =
    E.div(
      E.span(s"Elapsed: ${state}ms"),
      E.button(A.onClick(_ => instance.setState(0)), "Reset"),
    )

Lifecycle hooks

StatefulComponent provides lifecycle hooks for side effects. didMount runs after the first render; willUnMount is the place to drop timers or subscriptions:

import preactile.*

object DataFetcher extends StatefulComponent[Unit, String]:
  override def initialState(props: Unit): String = "Loading..."

  override def didMount(instance: Instance): Unit =
    instance.setState("Data loaded!")

  override def willUnMount(instance: Instance): Unit =
    ()

  def render(props: Unit, state: String, instance: Instance): VNode =
    E.p(state)

See the Examples page for the other live demos.