# `Filament.Hooks`

Hooks for Filament components.

## Application-facing hooks

Call these at the top level of `render/1`:

  - `use_state/1` — local mutable state; returns `{value, setter}`
  - `use_observable/1` — resolves a server reference to a pid (or nil when disconnected)
  - `use_observable/2` — resolves a server and projects its state; fn receives `:disconnected` when unavailable
  - `use_effect/2` — side-effect with optional cleanup
  - `memo_at/3` and `event_at/2` — invoked by compiler-generated code from `~F` templates

## Pattern: use_observable/1 + use_observable/2

Resolve the server once with `/1`, then project from it with `/2`. This lets you pass
the server pid to child components and apply multiple projections from the same process:

    def render(%{session_id: session_id}) do
      server = use_observable(fn -> MyServer.start_link(session_id) end)
      count  = use_observable(server, fn
        :disconnected -> 0
        state -> state.count
      end)
      label  = use_observable(server, fn
        :disconnected -> ""
        state -> state.label
      end)
      ...
    end

Passing the server as a prop lets child components project their own values without
creating redundant subscriptions:

    <ChildComponent server={server} />

    # In the child:
    def render(%{server: server}) do
      value = use_observable(server, fn
        :disconnected -> nil
        s -> s.some_field
      end)
      ...
    end

## Rules of hooks

1. Only call hooks at the top level of `render/1` — not inside `if`, `case`, or comprehensions.
2. Hooks must be called during a render pass (a `RenderContext` must be active).
3. Hook identity is determined by call order (slot index). Conditional hooks corrupt state.

# `use_effect`

```elixir
@spec use_effect((-&gt; (-&gt; term()) | nil), deps :: [term()] | :always) :: :ok
```

Schedules a side effect to run after the render completes.

effect_fn is called after the component renders. It may return a cleanup function
(zero-arity fn returning :ok or any term) that is called:
- Before the next time the effect runs (when deps change), OR
- When the fiber unmounts

deps controls when the effect re-runs:
- [] — run once on mount, cleanup on unmount
- [dep1, dep2] — run when any dep changes (Kernel.== comparison), cleanup before re-run
- :always — run on every render

Rules: call only at the top level of render/1.

# `use_observable`

```elixir
@spec use_observable(
  server_or_fn ::
    GenServer.server() | (-&gt; pid() | {:ok, pid()} | GenServer.server())
) :: pid() | GenServer.server() | nil
```

Resolves an observable server reference to a pid, without subscribing.

The argument can be any of:
- a pid, atom, `{:via, ...}`, or `{node, name}` — used directly as the server
- a zero-arity function — called on first connect (and again if the process dies) to
  obtain a pid or `{:ok, pid}`; useful when the component owns the server's lifecycle

Returns `nil` during disconnected (HTTP static) mounts. On subsequent renders,
reuses an existing pid if still alive; restarts a factory fn otherwise.

Use this hook when you want to pass the server identity to child components or
apply multiple projections from the same server via `use_observable/2`.

Must be called at the top level of `render/1` in consistent order (like all hooks).

# `use_observable`

```elixir
@spec use_observable(
  server_or_fn ::
    GenServer.server() | (-&gt; pid() | {:ok, pid()} | GenServer.server()),
  project :: (term() | :disconnected -&gt; term())
) :: term()
```

Resolve an observable server and project its state into a value.

The first argument is a server reference (same as `use_observable/1`). The second
argument is a projection function called on every state update from the server. When
the server is unavailable (disconnected HTTP mount or nil), the function is called
with the atom `:disconnected` so it can return a safe default:

    count = use_observable(CartServer, fn
      :disconnected -> 0
      state        -> state.count
    end)

Passing the server as a prop lets a parent resolve the process once and share it with
children that each apply their own projection:

    # Parent
    server = use_observable(fn -> MyServer.start_link([]) end)
    <Child server={server} />

    # Child
    value = use_observable(server, fn
      :disconnected -> nil
      s             -> s.some_field
    end)

Must be called at the top level of `render/1` in consistent order (like all hooks).
Do not call inside conditionals or loops.

# `use_state`

```elixir
@spec use_state(initial :: term()) :: {value :: term(), setter :: (term() -&gt; :ok)}
```

Returns the current state value and a setter function.

On the first render of this fiber, returns {initial, setter}.
On subsequent renders, returns the most recently set value (or initial if never changed).

The setter is a closure. Call it from event handlers or effects to trigger a re-render.
Calling the setter sends a message to the owning LiveView process, which re-renders
the affected fiber.

Rules: call only at the top level of render/1. Do not call inside conditionals.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
