Raxol.Core.Runtime.ProcessComponent (Raxol v2.8.0)

View Source

A GenServer wrapping a single component module for crash isolation.

Each ProcessComponent runs in its own process under Raxol.DynamicSupervisor, so a crash in one component does not bring down the rest of the application.

The rendering engine that draws a process_component/2 node owns its process: it starts it on the first frame the node is in the view, keeps it (and the component's state) across frames, and stops it when the node leaves the view or the engine stops. A process also stops when its owner (:parent_pid, required) exits. The supervisor does not restart a crashed component; the frame draws a placeholder in its place and a later frame starts it again with fresh state from init/1.

Start-up

start_link/1 only checks that the module is loaded (an unloadable module is {:error, {:module_not_available, module, reason}} and no process is started) and returns as soon as the process exists. init/1 and mount/1 run afterwards inside the new process, so a slow init/1 holds up neither the caller nor the supervisor that starts every component in the VM. Once both have run, the owner receives {:process_component_ready, pid}, once; until then the engine draws a "starting" placeholder for the node. A call such as get_render_tree/2 made before that simply waits.

init/1 must return {:ok, state} or a bare map. {:error, reason} stops the process with reason; anything else stops it with {:bad_init_return, value}. Nothing is silently turned into empty state.

Usage

Components used with ProcessComponent must implement:

  • init/1 - receives props, returns {:ok, state}, a state map, or {:error, reason}
  • render/2 - receives state and context, returns element tree
  • update/2 (optional) - receives message and state, returns new state
  • update_props/2 (optional) - receives the node's changed props and the state, returns new state. A component that exports it keeps its state across props changes; one that doesn't is re-initialised from the new props through init/1 (and mount/1) in the same process, after unmount/1 has seen the old state.
  • mount/1 (optional) - receives the initial state after init/1, returns the state to continue with
  • unmount/1 (optional) - the mirror of mount/1: receives the live state when the component stops for any reason (its node left the view, the engine or owner stopped, the supervisor shut it down, a crash) or just before a re-init. Its return value is ignored, and an exception raised in it is logged and does not change why or whether the component stops. It does not run for a component whose init/1 failed.

Crash reports and :sys.get_status/1 show the component module and id only; its state and props are redacted, since props routinely carry credentials.

Use the process_component/2 View DSL helper to embed process components:

process_component(MyHeavyWidget, %{path: "/tmp"})

Summary

Functions

Returns a specification to start this module under a supervisor.

Replaces the component's props in its running process. A component that exports update_props/2 gets update_props(props, state) and keeps the state it returns; any other has unmount/1 called on its current state and is re-initialised from props through init/1 (and mount/1, if exported). A re-init whose init/1 fails stops the process with that reason, and this call exits.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

get_render_tree(pid, context)

send_update(pid, message)

start_link(opts)

update_props(pid, props)

Replaces the component's props in its running process. A component that exports update_props/2 gets update_props(props, state) and keeps the state it returns; any other has unmount/1 called on its current state and is re-initialised from props through init/1 (and mount/1, if exported). A re-init whose init/1 fails stops the process with that reason, and this call exits.