#48
feat: added experimental React-style function components
Summary
Added an opt-in function component API for #27, allowing components to use persistent state and lifecycle hooks while keeping Rake’s existing view! syntax.
The implementation is behind experimental-components. It remains experimental because render isolation and performance acceptance are unresolved.
Implementation
Component API
- Added
#[rake::component]to generate props and a retained GPUI view. - Preserved
<Counter ... />and{..defaults}syntax. - Supported renamed Rake dependencies.
- Required cloneable props, with
PartialEqrequired only for#[rake::component(memo)].
State and lifecycle
- Added
Scope,use_state,use_ref,use_memo, anduse_effect. - Stored hooks on the component’s retained entity, preserving state across renders and keyed reordering.
- Used component type, parent identity, and an explicit stable ID to distinguish instances.
- Added Copy handles backed by a generation-aware registry with weak component ownership.
- Returned disposal errors when handles outlive their components.
- Added hook-order, kind, and type checks.
- Deferred effects until after paint, with cleanup before replacement and on unmount.
Integrations
- Added context and theme observation through existing GPUI globals.
- Added resource hooks backed by Rake’s managed loading system.
- Added bounded hook metadata to the inspector and CDP.
- Shared handle and resource-access logic to reduce duplication.
- Added examples, documentation, and comparison benchmarks.
Activity
4Sure I'm still up.
Hey @theobiabo, first, an apology. You asked to take this issue, I said yes, and then after you'd put in all this work I changed the design under you. That's on me, not you. I should have told you on the issue as soon as I started rethinking hooks.
The work itself is really good. The report is unusually honest about where it falls short: the performance gate, the render isolation limits, what wasn't measured. That's exactly what I wanted from #27.
What changed: while reviewing the design I realised hooks fight GPUI's model, so #27 now uses signals instead. They're
Copyhandles over GPUI entities, setup runs once and the tail expression renders, and there are no rules of hooks. That means the hooks API can't merge as it is, but a lot of your work carries over directly:RenderOncesibling collisionYour finding that equal props can't skip rendering without
.cached()and fixed sizes is also what shaped the caching plan in the new issue.If you're still up for it, I'd love for you to keep going on the signals version. If you'd rather not after this, that's completely understandable, and thank you either way. Happy to answer any design questions on the issue.