Declarative UI Programming
What is Declarative?
State your intent !
Why does it matter?
-
nobody knows what they want:
-
stakeholders use vague language that doesn't nail down the problem
-
programmers use strict language that could, but:
-
-
programmers prefer working on everything else
What tangible problem are we trying to solve?
Iterate better UX with less effort.
What is UI?
-
the software, from the user perspective
-
a nightmare, from the developer perspective
Model/View Split
- Pro: business rules and presentation are separated; new UI can be built on top of existing models
- Con: you now have a new problem, i.e. consistency
Model View C...
- ...ontrollerfest
- ...atastrophy
Countless attempts to fix it:
HMVC, MVP, MMVC, MVVM
Do they though?
Much ado for nothing
- anemic domain model
- dense controller jungle
- fragile presentation layer
MVC fail at Docler
- camgirls website with 35m daily visitors (market leader)
- video + chat component (UI centric) was written in flash and ported to Haxe
- result: 6MB JS file with barely more than core functionality and tons of bugs delivered
What went wrong?
- real time video streaming on browsers is a mess
- strict, dogmatic use of Spring like MVC + IOC architecture on all levels of abstraction
- attempts to stay decoupled backfired
We made Rick Astley proud!
Begin with what's most important: UI
What if we would start over?
React: UI is a function of State
class Counter extends React.Component {
state = { count: 1 };
render() {
return <button onClick={handleClick}>
{this.state.count}
</button>
}
handleClick = () => this.setState({
count: this.state.count + (this.props.step | 1)
})
}
<Counter step={10} />
<Counter />
All internal state is in the state object and setState updates and schedules redraw.
Comparison to classical views
-
Same: State change invalidates component, which then updates.
-
Different: A React view uses the same code for creating and updating a component (via vdom diffing).
Composition: Lifting State Up
Single temparature input:
What if we need multiple ones?
class TemperatureInput extends React.Component {
state = { value: 0 }
render() {
<label>
<input
type="number"
value={this.state.value}
onChange={e => this.setState({ value: e.target.valueAsNumber })}
/>
°C
</label>
}
}
Step 1: externalize state
It's all in the props now.
class TemperatureInput extends React.Component {
render() {
<label>
<input
type="number"
value={this.props.value}
onChange={e => this.props.onChange(e.target.valueAsNumber)}
/>
{this.props.unit}
</label>
}
}
Step 2: let it trickle down
class Converter extends React.Component {
state = { kelvin: 293.15 };
render() {
return <div>
<TemperatureInput
value={kToF(this.state.kelvin)} unit="°F"
onChange={(value) => this.setState({ kelvin: fToK(value) })}
/>
<TemperatureInput
value={kToC(this.state.kelvin)} unit="°C"
onChange={(value) => this.setState({ kelvin: cToK(value) })}
/>
<TemperatureInput
value={this.state.kelvin} unit="K"
onChange={(value) => this.setState({ kelvin: value })}
/>
</div>
}
}
Problems
-
The whole subtree invalidates
-
The root has both business and presentation logic
Solution: state containers
-
Purely functional: Redux
-
Object oriented: MobX
Redux
typedef Reducer<State, Action> = State->Action->State;
Congratulation, you've reinvented state machines \o/
Usage: Create pure view functions and perform occult ceremonies to connect them to the store.
ProTip: it's a useful pattern in moderation.
MobX
Model your data as objects.
Decorate models with @observable and @computed
and views with @observer.
While rendering, views subscribe to changes of the data they rendered and update accordingly.
Full Frameworks
- Vue (the new hype)
- Svelte (much like Vue, but cleverer)
- Imba (much like svelte, but with its own language)
- coconut (pure Haxe goodness)
React has made it beyond hype
- Funded by Facebook
- React Native project for native iOS and Android UI
- Microsoft just published React Native Windows
- It has decent Haxe bindings!
React is usually fast enough
React + MobX + custom renderer
So,
what's
Coconut?
A Search for the Holy Grail of UI Programming
What I though I was getting myself into
High level overview
Full React Interop
- Can use React components within coconut and coconut views as React components
- Access to vast React ecosystem, e.g. material-ui
- Can target React Native
- Switched to React at Docler for interop with other teams:
89 files changed, 376 insertions(+), 427 deletions(-)
Hello, TodoMVC
- 28 KB minified
- outscales preact on preact's benchmark
Some more numbers
Other backends
-
OpenFl: exists merely as tutorial for now, but once Haxe FeathersUI is public, it's going to be a thing!
- HaxeUI: waiting for "new-component-method" branches to be merged
- Pixi.js or Heaps / h2d / DomKit: hopefully by Mark Knol
- native Android: as soon as genjvm works!
- whatever you want ;)
Time
for a
Demo ...
Declarative UI
By Juraj Kirchheim
Declarative UI
- 1,863