1# View Example — Plan
2
3## Done
4
5- Introduced `View` trait, `ComponentView` trait, and `ViewElement` struct as a unification of `Component`, `RenderOnce`, `AnyView`, and `Render`
6- Initialized example of the composition this can achieve with the editor
7- Made `ExampleInput` a proper `View` with its own `ExampleInputState` entity — demonstrates independent caching boundaries, focus tracking, and separation of concerns
8- Made `ExampleTextArea` a `ComponentView` — demonstrates stateless wrapper pattern where the inner `EntityView` does the caching
9- Analyzed `use_state` observer behavior — confirmed it's necessary and correct for component-local state (non-view entities aren't in the dispatch tree, so `mark_view_dirty` can't propagate)
10
11## Next
12
13- Add a render log showing coarse-grained caching — log `render()` calls on ExampleInput, ExampleTextArea, and ExampleEditor to show that sibling isolation works and FlashState only re-renders chrome
14- RootView type — a window root that takes an `Entity<V>` directly. Introduce in a backwards-compatible way alongside `open_window`. Use in this example.
15- Move focus handles out to the input and textarea, stop blinking when not focused
16- Tab index support so the demo doesn't need mouse movement
17- De-fluff LLM generated code
18
19## Design Decisions
20
21### View as a separation-of-concerns boundary
22
23The `ExampleInput` View demonstrates the key principle: a View should own its concerns and delegate everything else.
24
25- `ExampleInputState` creates the editor internally (`cx.new(|cx| ExampleEditor::new(cx))`) — the parent never sees it
26- Focus is tracked via `on_focus`/`on_blur` listeners on the state entity — render never reads the editor
27- The editor is passed as a `ViewElement` child and trusted to manage its own rendering (blink, text, cursor)
28- `render()` only reads from `self.state` (ExampleInputState), giving it a clean reactive boundary
29
30The parent just does:
31```
32let input_state = window.use_state(cx, |window, cx| ExampleInputState::new(window, cx));
33ExampleInput::new(input_state).width(px(320.)).color(input_color)
34```
35
36### `use_state` as the allocation pattern for View entities
37
38`use_state` in the parent creates the View's backing entity. The View's `new()` is a pure constructor that takes the entity handle. The entity's `new()` can internally chain to `cx.new()` to create child entities (like the editor), keeping allocation hierarchical and self-contained.
39
40### `use_state` observer — keep as-is
41
42`use_state` creates an observer that notifies the parent view when the state entity changes. This is correct because:
43
441. Non-view entities aren't in the dispatch tree, so `mark_view_dirty` can't propagate their notifications upward
452. `use_state` entities are component-local (single owner), so notifying the parent is always the right granularity
463. The alternative (fixing the tracking layer to auto-propagate all entity reads) would be catastrophic for large shared entities like `Project`, where dozens of views read it but only care about specific changes
47
48### Entity taxonomy for reactivity
49
50| Entity type | On notification | Mechanism |
51|---|---|---|
52| View entity (ExampleEditor) | Own ViewElement cache invalidates | `dirty_views` + dispatch tree |
53| Component-local (use_state) | Parent view re-renders | `use_state` observer |
54| Shared, universally relevant (Theme) | All readers re-render | Explicit `observe` today; `use_global` (v2) |
55| Shared, selectively relevant (Project) | Only subscribers re-render | Explicit `subscribe` |
56
57### View vs ComponentView vs EntityView
58
59- **View** = has its own entity, gets caching, creates a reactive boundary. The component owns its state and delegates child rendering. Use when the component has state or when caching its render output matters for performance (ExampleInput).
60- **ComponentView** = stateless wrapper, no caching, always re-renders when parent does. Use when the component is cheap and the inner child has its own cache (ExampleTextArea).
61- **EntityView** = the entity IS the view. The data-owning entity renders itself directly. Use for entities that need full control over their element tree (ExampleEditor).
62
63### V2: `use_global` for universal reactivity
64
65A future `cx.use_global::<ThemeSettings>()` API that auto-wires reactive dependencies for globally-shared "value" entities (theme, settings). Would eliminate dozens of boilerplate `observe_global` + `cx.notify()` callbacks throughout the codebase.