Skip to content

Hooks

Hooks

Hooks let function components hold state, run side effects, and memoize values — no class required. If you have used React hooks, the API will feel immediately familiar.

Rules of hooks

  1. Call hooks at the top level of your function component.
  2. Do not call hooks inside loops, conditions, or nested functions.
  3. Do not call hooks outside a function component's render.

These rules exist because hooks rely on a stable call order to map each hook call to its stored state slot.


use_state

Declare a piece of component-local state. Returns (value, setter).

from wybthon import h, use_state

def Counter(props):
    count, set_count = use_state(0)
    return h("div", {},
        h("p", {}, f"Count: {count}"),
        h("button", {"on_click": lambda e: set_count(count + 1)}, "+1"),
    )

The setter accepts a plain value or an updater function that receives the previous value:

set_count(42)                  # set directly
set_count(lambda prev: prev+1) # updater

You can also pass a callable as the initial value; it will be invoked once on the first render (lazy initialization):

val, set_val = use_state(lambda: expensive_default())

use_effect

Register a side-effect that runs after the component renders.

deps argument Behavior
None (default) Runs after every render
[] Runs only on mount
[a, b] Runs when a or b changes

Return a cleanup function from the effect to run before the next execution or on unmount:

from wybthon import h, use_state, use_effect

def Timer(props):
    seconds, set_seconds = use_state(0)

    def setup():
        from js import setInterval, clearInterval
        from pyodide.ffi import create_proxy

        proxy = create_proxy(lambda: set_seconds(lambda s: s + 1))
        tid = setInterval(proxy, 1000)

        def cleanup():
            clearInterval(tid)
            proxy.destroy()
        return cleanup

    use_effect(setup, [])  # mount-only

    return h("p", {}, f"Elapsed: {seconds}s")

use_memo

Memoize an expensive computation. Re-computes only when deps change.

filtered = use_memo(lambda: [x for x in items if x.active], [items])

use_ref

Create a mutable container that persists across renders without causing re-renders when mutated.

ref = use_ref(None)
# ref.current can be read/written freely

use_callback

Memoize a callback so child components receiving it as a prop can skip unnecessary re-renders.

handle_click = use_callback(lambda e: set_count(count + 1), [count])

Hooks vs. class components

Function + hooks Class component
State use_state self.count = signal(0)
Side effects use_effect on_mount / on_cleanup
Memoization use_memo manual
Refs use_ref instance attributes

Both styles are fully supported; choose whichever fits your situation. For new code, hooks are recommended because they are more concise and composable.