Skip to content

Commit 7421a85

Browse files
committed
Sketching out alternatives
1 parent 8800702 commit 7421a85

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

rfcs/2024-05-21-ui-component-model.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,72 @@ Soft goals:
8989

9090
When I give Claude the requirements and ask it to design a component, it usually produces something like this:
9191

92+
```jsx
93+
const [count, setCount] = signal(0)
94+
const [clicks, setClicks] = stream()
95+
clicks.sink(_ => setCount(count() + 1))
96+
97+
function Counter() {
98+
return <a onclick="{setClicks}">The count is: {count}</a>
99+
}
100+
```
101+
102+
Note the lifetimes:
103+
104+
- Signals are constructed once (maybe in place, or maybe imported from the runtime somehow).
105+
- The counter function is called by the framework every time the count signal changes.
106+
107+
Pros and cons:
108+
109+
- Pro: lots of React-style functional components in the training set
110+
- Con: JSX produces a fully dynamic VDOM. The tree can arbitrarily change across calls
92111

112+
Prior art:
113+
114+
- https://preactjs.com/blog/introducing-signals/
93115

94116
### Spellcaster-style functional components
95117

118+
[Spellcaster](https://github.com/gordonbrander/spellcaster) uses a
119+
120+
```js
121+
export function Counter() {
122+
const [count, setCount] = signal(0)
123+
const [clicks, setClicks] = stream()
124+
clicks.sink(_ => setCount(count() + 1))
125+
126+
return a(
127+
{onclick: setClicks},
128+
text(() => `The count is ${count()}`)
129+
)
130+
}
131+
```
132+
133+
Note the lifetimes:
134+
135+
- Counter function is called once at program start, to construct the tree.
136+
- The FRP signals create bindings to specific parts of the tree to update them reactively
137+
138+
The tree returned is largely static, with dynamic FRP bindings in specific places.
139+
140+
Pros and cons:
141+
142+
- Pro: vanilla JS
143+
- Pro: static tree
144+
- Con: less of this in the training set, but then again hyperscript is pretty common
145+
146+
Alternatively this lifetime semantics could be used with JSX instead of hyperscript:
147+
148+
```jsx
149+
export function Counter() {
150+
const [count, setCount] = signal(0)
151+
const [clicks, setClicks] = stream()
152+
clicks.sink(_ => setCount(count() + 1))
153+
154+
return <a onclick="{setClicks}">The count is: {count}</a>
155+
}
156+
```
157+
96158
### Stateless templates
97159

98160
Borrowing ideas from Mustache, Vue, and Svelte, we could separate logic from template. This would make the template a pure function. It would also encourage factoring out the logic into signal transformations.

0 commit comments

Comments
 (0)