Beispiel #1
0
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
  }
}
Beispiel #2
0
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)
        }
      })
  }
}