Skip to content

Instantly share code, notes, and snippets.

@tinchoz49
Last active March 10, 2017 21:24
Show Gist options
  • Save tinchoz49/fb2751edca29806b6c4e2f21b1b75fec to your computer and use it in GitHub Desktop.
Save tinchoz49/fb2751edca29806b6c4e2f21b1b75fec to your computer and use it in GitHub Desktop.
subpatching with snabbdom and flyd
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