# Nodes and the walk

> The Node trait, the router that holds your nodes, and how the path is derived each frame.

The table is static. The path is live. The router turns one into the other each frame by asking the nodes on the path which child, if any, they are showing.

## The router

```rust
let routes: Vec<(RouteId, Box<Screen>)> = vec![("home", Box::new(Home))];
let router = Router::new(table, routes, |_: &Model| "home")?;
```

`Router<Cx, A, R>` has three type parameters:

- `Cx` is your context: the model the nodes read. In the examples it is `Model`.
- `A` is your action type, the thing nodes push onto the [outbox](/docs/state/intents).
- `R` is what each id resolves to. Usually `dyn Node<Cx, A>`, so each id can have its own handler type.

`Router::new` takes the table, one handler per id, and a root function. The root function picks which root a path starts at. In a one-root app it returns a constant. In a larger app it is your lifecycle: signed out, loading, ready.

## The walk

`router.resolve(cx, store)` derives the path:

1. Call the root function. That id is the first segment.
2. Ask that node's `resolve_child_in(cx, store)`. By default this calls `resolve_child(cx)`.
3. If it returns `None`, the path ends here.
4. If it returns a child, check the child is registered beneath this node and that the child's [guard](/docs/routing/guards) allows entry. If so, the child is the next segment. Go to 2.

The path is **derived, never pushed**. There is no stack to get out of step with your state, because the path is recomputed from your state every time.

A node names **at most one** child, because `resolve_child` returns an `Option<RouteId>`. That makes the walk total and ordered with nothing above it to arbitrate. If you have four overlays that must never show together, you do not need four booleans and a rule; you need one `Option` that cannot hold two values.

```rust
fn resolve_child(&self, m: &Model) -> Option<RouteId> {
    m.home_child
}
```

If a node names a child the table does not register beneath it, or names an id already on the path, the walk stops there and prints one warning per bad pair to stderr. The frame still renders, as if the node had named nothing.

## Reading claims from the store

`resolve_child_in` gets the store as well as your context. Override it when the claim is a store field rather than a model field, for example when another part of the app writes "show this child" into a node's scope. The store it sees is the last commit, never a half-finished tick.

## The Node trait

| Method | Called when |
| --- | --- |
| `resolve_child(&self, cx)` | Every walk, while the node is on the path |
| `resolve_child_in(&self, cx, store)` | Every walk. Defaults to `resolve_child` |
| `occludes(&self)` | When the table declares no occlusion for this id |
| `update(&mut self, cx, out, dt)` | `Router::update`, outermost first |
| `escape(&mut self, cx, out)` | Escape is offered to this node |
| `take_leave_request(&mut self)` | Your binding asks after delivering an event |
| `child_popped(&mut self, out, child)` | A child this node was claiming popped |
| `enter(&mut self, cx)` | The id joined the path |
| `leave(&mut self, cx)` | The id left the path |
| `depart(&mut self, to)` | This node is the deepest one leaving |

Methods that run during a frame (`update`, `escape`, `child_popped`) get `&Cx` and an `&mut Outbox<A>`. They can read and they can ask, but they cannot change your model. Only `enter` and `leave` get `&mut Cx`.

> The trait is scaffolding. It was cut from Masque's routing layer at the seam where the remaining methods named UI types, and some methods (`occludes`, `take_leave_request`) are expected to move or retire. Expect it to change before 1.0.

## Lifecycle

`enter` and `leave` mean the id **joined or left the path**, not that it became or stopped being the deepest. When a prompt opens above an editor, the editor stays on the path, so its `leave` does not run and whatever it was holding stays put.

`resolve` runs `leave` deepest-first and `enter` outermost-first, so a child lets go before its parent, and a parent is in place before its child. It returns `true` when the path changed.

Node objects themselves are not dropped when they leave. The router holds one handler per id for its whole life. Use `leave` to reset anything that should not survive. If state must outlive the screen it is edited in, it is not node state: put it in your model or a longer-lived [scope](/docs/state/scopes).

## Reading the router

| Method | Returns |
| --- | --- |
| `path()` | The active path, outermost first |
| `deepest()` | The deepest active id |
| `is_active(id)` | Whether `id` is on the path |
| `drawing()`, `visible_views()` | What renders, after [occlusion](/docs/routing/occlusion) |
| `refused()` | The door the last walk was refused at |
| `last_departure()` | How the last path change asked to leave |
| `get(id)`, `get_mut(id)` | The handler for `id` |
| `table()` | The table |

## Next

[Escape, popping and departure](/docs/routing/escape): how a screen leaves when the path is derived.
