Last active
March 10, 2017 21:24
-
-
Save tinchoz49/fb2751edca29806b6c4e2f21b1b75fec to your computer and use it in GitHub Desktop.
subpatching with snabbdom and flyd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const flyd = require('flyd'); | |
const snabbdom = require('snabbdom'); | |
const htmldomapi = require('snabbdom/htmldomapi').default; | |
htmldomapi.removeChild = (node, child) => { | |
if (node && child) { | |
node.removeChild(child); | |
} | |
}; | |
const patch = snabbdom.init([], htmldomapi); | |
const h = require('snabbdom/h').default; | |
const noop = (() => {}); | |
function start(container, createApp) { | |
const forceUpdate$ = flyd.stream(); | |
const app = createApp(forceUpdate$); | |
app.state = flyd.stream(app.state); | |
const render$ = flyd.immediate(flyd.combine(state => state(), [app.state, forceUpdate$])); | |
window.state = app.state; | |
return flyd.scan(last => patch(last, app.view()), container, render$); | |
} | |
function createRenderer(parentForceUpdate$) { | |
return elem => component(elem, parentForceUpdate$); | |
} | |
function component(createElem, parentForceUpdate$) { | |
let inDOM = false; | |
let init = true; | |
const forceUpdate$ = flyd.stream(); | |
const elem = createElem(forceUpdate$); | |
elem.state = flyd.stream(elem.state); | |
const render$ = flyd.immediate(flyd.combine(state => state(), [elem.state, forceUpdate$])); | |
const vnode$ = flyd.scan(last => { | |
let vnode = elem.view(); | |
if (!vnode) { | |
init = false; | |
return vnode; | |
} | |
vnode.data.hook = vnode.data.hook || {}; | |
const userInsert = vnode.data.hook.insert || noop; | |
const userUpdate = vnode.data.hook.update || noop; | |
const userDestroy = vnode.data.hook.destroy || noop; | |
vnode.data.hook.insert = vnode => { | |
inDOM = true; | |
init = false; | |
userInsert(vnode); | |
}; | |
vnode.data.hook.update = (oldVnode, vnode) => { | |
userUpdate(vnode); | |
}; | |
vnode.data.hook.destroy = vnode => { | |
inDOM = false; | |
userDestroy(vnode); | |
}; | |
if (inDOM) { | |
vnode = patch(last, vnode); | |
} | |
return vnode; | |
}, null, render$); | |
flyd.on(vnode => { | |
if (!init && ((!vnode && inDOM) || (vnode && !inDOM))) { | |
parentForceUpdate$(true); | |
} | |
}, vnode$); | |
return vnode$; | |
} | |
function counter(forceUpdate$) { | |
const renderer = createRenderer(forceUpdate$); | |
const clock1 = renderer(clock); | |
return { | |
state: { | |
count: 0 | |
}, | |
insert() { | |
const state = this.state; | |
setInterval(() => { | |
state({ count: state().count + 1 }); | |
}, 2000); | |
}, | |
view() { | |
return this.state().count < 4 ? | |
h('h2', { | |
hook: { | |
insert: this.insert.bind(this) | |
} | |
}, [ | |
this.state().count, | |
clock1() | |
]) : | |
undefined; | |
} | |
}; | |
} | |
function clock() { | |
return { | |
state: { | |
count: 0 | |
}, | |
insert() { | |
const state = this.state; | |
setInterval(() => { | |
state({ count: state().count + 1}) | |
}, 1000); | |
}, | |
view() { | |
return this.state().count < 2 ? | |
h('h2', { | |
hook: { | |
insert: this.insert.bind(this) | |
} | |
}, this.state().count) : | |
undefined; | |
} | |
} | |
} | |
const app = function (forceUpdate$) { | |
const renderer = createRenderer(forceUpdate$); | |
const counter1 = renderer(counter); | |
const counter2 = renderer(counter); | |
return { | |
state: { | |
title: 'el titulo' | |
}, | |
view() { | |
return h('div', [ | |
h('h1', this.state().title), | |
h('p', 'text'), | |
counter1(), | |
h('div', [ | |
h('div', [ | |
counter2() | |
]) | |
]) | |
]); | |
} | |
}; | |
}; | |
start(document.getElementById('app'), app); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment