Last active
March 1, 2019 21:26
-
-
Save tokland/6ae023f89d0a03f7aa38632f3b13b340 to your computer and use it in GitHub Desktop.
Stateful component wrapper over wavesoft/dot-dom with a functional reducer (hooks supported)
This file contains 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
/* Stateful component wrapper over wavesoft/dot-dom with a functional reducer (hooks supported) */ | |
function mapValues(input, mapper) { | |
return Object.keys(input).reduce((acc, key) => { | |
const value = mapper(input[key], key); | |
return value ? Object.assign(acc, { [key]: value }) : acc; | |
}, {}); | |
} | |
function _setState(setState, newState$) { | |
if (newState$) { | |
Promise.resolve(newState$).then(newState => setState(newState)); | |
} | |
} | |
function statefulComponent({ view, initialState, actions }) { | |
return (props, state_, setState, hooks) => { | |
const state = Object.keys(state_).length === 0 ? initialState(props) : state_; | |
const actionHandlers = mapValues(actions, (action, key) => { | |
switch (key) { | |
case "onMount": | |
hooks.m.push(domEl => _setState(setState, action(state, props, domEl))); | |
return null; | |
case "onUnmount": | |
hooks.u.push(() => _setState(setState, action(state, props))); | |
return null; | |
case "onUpdate": | |
hooks.d.push((domEl, prevDomEl) => | |
_setState(action(setState, state, props, domEl, prevDomEl)) | |
); | |
return null; | |
default: | |
return (...eventArgs) => _setState(setState, action(state, props, eventArgs)); | |
} | |
}); | |
return view(props, state, actionHandlers); | |
}; | |
} | |
/* Example: a Counter */ | |
function wait(ms) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
const Counter = statefulComponent({ | |
initialState: props => ({ | |
clicks: props.initialValue || 0, | |
}), | |
actions: { | |
// How to use dot-dom lifecycle events: mount, unmount, update | |
onMount: (state, props, domElement) => console.log("mounted", { state, props, domElement }), | |
onUpdate: (state, props, domElement, previousDomElement) => | |
console.log("update", { state, props, domElement, previousDomElement }), | |
onUnmount: (state, props) => console.log("unmounted", { state, props }), | |
// Synchronous action, return the new state | |
increment: state => ({ clicks: state.clicks + 1 }), | |
// Asynchronous action, return a promise that resolves to the new state | |
incrementWithDelay: state => wait(1000).then(() => ({ clicks: state.clicks + 1 })), | |
}, | |
view: (_props, state, handlers) => { | |
return H("div", [ | |
H("span", `Value: ${state.clicks}`), | |
H("button", { onclick: handlers.increment }, `Increment`), | |
H("button", { onclick: handlers.incrementWithDelay }, `Increment with 1s delay`), | |
]); | |
}, | |
}); | |
function main() { | |
R(H(Counter, { initialValue: 1 }), document.body); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment