function vdux ({middleware = [], reducer, initialState = {}, app, ready = () => true}) { /** * Create redux store */ const store = applyMiddleware(string, local('ui'), component(), ...middleware)(createStore)(mount('ui', reducer), initialState) /** * Initialize virtex */ const {create, update} = virtex(store.dispatch) /** * Create the Virtual DOM <-> Redux cycle */ return new Promise((resolve, reject) => { run() const unsub = store.subscribe(() => setTimeout(run)) function run () { try { const state = store.getState() const vtree = app(state) const html = render(vtree) if (ready(state)) { resolve({html, vtree, state}) unsub() } } catch (e) { reject(e) } } }) let prev function render (vtree) { const result = (prev ? update(prev, vtree) : create(vtree)).element prev = vtree return result } }
function vdux ({middleware = [], reducer, initialState = {}, app, node = document.body, prerendered}) { /** * Create redux store */ let vtree const dirty = {} const components = {} const store = applyMiddleware(dom, local('ui', dirty), component(components), ...middleware)(createStore)(mount('ui', reducer), initialState) /** * Initialize virtex */ const {create, update, updatePaths} = virtex(store.dispatch) /** * Empty the root node */ if (!prerendered) { empty(node) } /** * Render the VDOM tree */ vtree = render() prerendered ? create(vtree, '0', node.firstChild) : node.appendChild(create(vtree).element) /** * Create the Virtual DOM <-> Redux cycle */ const unsubscribe = store.subscribe(sync) const undelegate = delegant(document, maybeDispatch) const undelegateGlobal = delegateGlobal(window, maybeDispatch) return { replace (_app, _reducer) { app = _app reducer = _reducer store.replaceReducer(mount('ui', reducer)) sync() }, stop () { unsubscribe() undelegate() undelegateGlobal() } } function maybeDispatch (action) { return action && store.dispatch(action) } /** * Render a new virtual dom */ function render () { return app(store.getState()) } /** * Sync the virtual dom and the actual dom */ let pending = false function sync () { // Prevent re-entrant renders if (pending) return pending = true setTimeout(syncNow) } function syncNow () { pending = false const newTree = render() update(vtree, newTree) updateDirty() vtree = newTree } function updateDirty () { Object .keys(dirty) // Sort by shortest dirty paths first, so that if possible // we get some of the higher re-renders cleaning up some // of the lower ones .sort((a, b) => a.length - b.length) .forEach(path => { // Check that it's still dirty, since the re-rendering of a higher component // may cause one of the lower ones to get re-rendered if (dirty[path]) { const component = components[path] const prev = {...component} component.vnode = null update(prev, component, path) } }) } }