Live Demo¶
Simple Counter¶
This is about as small demo as possible:
The template:
import {Counter, counter} from './counter'
view main():
<p>
store @cnt = Counter
<button>
link {click} counter(1) -> @cnt
"+"
<input disabled size="6" value=@cnt>
<button>
link {click} counter(-1) -> @cnt
"-"
Bootstrap code (index.js):
import {createStore, applyMiddleware} from 'redux'
import {attach} from 'khufu-runtime'
import {main} from './counter.khufu'
attach(document.getElementById('app'), main(), {
store(reducer, middleware, state) {
return createStore(reducer, state,
applyMiddleware(...middleware))
}
})
And the counter store counter.js.
export function Counter(state=0, action) {
switch(action.type) {
case 'incr':
return state + action.value;
default:
return state;
}
}
export function counter(x) {
return { type: 'incr', value: x }
}
This is just simple redux store and an action creator.
Debounce¶
This example demonstrates use of redux-saga for debouncing the event stream:
This example shows scoping of stores and how to define stores and middlewares dynamically:
<div.table>
for ms of [0, 200, 1000]:
<div.row>
store @value = Text | delay_saga(ms)
<div.main>
store @id = id
<label for=@id>
"Delay "
ms
" ms: "
<input id=@id type="text">
link {blur, change, keyup, keydown} delay(update(this.value)) -> @value
<div.clone>
store @id = id
<label.arrow for=@id>
"⤷"
<input id=@id disabled value=@value>
And here is a delay_saga
middleware:
console.log("VALUE")
while(true) {
let action = yield take('delay')
let deadline = Date.now() + msec
let diff
while ((diff = deadline - Date.now()) > 0) {
let {event} = yield race({
event: take('delay'),
timeout: sleep(diff),
})
if(event) action = event;
}
yield put(action.action)
}
}
return {saga: debounce}
}
This looks more complex than stream.debounce(x)
from RxJS, but it’s
because we don’t use any function defined by library, but rather implementing
functionality ourselves. Sure you can use redux-rx or any other stuff.
It’s just a demo, khufu does not require to use redux-saga.
Components¶
Here is a simple demo for a component label that has a body
and
a badge
placeholders:
Template code is as simple as:
view label(){body, badge}:
<div.label>
body()
if badge:
<div.badge> badge()
view main():
label():
body: "Inbox"
badge: 10
label()
"Sent mails"
Error Handling¶
Following is a demo of error handling:
Note, how setting “bad value” breaks the rendering immediately. But setting a good value doesn’t do, unless retry action is also executed. This is because the code looks like this (stripped some details:
<div>
store @fruits = Fruits
store @has_error = Flag
if not @has_error:
catch * set_true() -> @has_error:
<b> @fruits.banana.price
else:
<button>
link {click} set_false() -> @has_error
"Retry"
<button>
link {click} good_value() -> @fruits
"Set good value"
I.e. if @fruits
store is updated, and @has_error
still contains
true
the catch block isn’t rerendered, but obviously if there was no error
and store value changed, the template will rerender and drop into an errorneous
state.