UNPKG

3.07 kBJavaScriptView Raw
1import curry from 'ramda/src/curry'
2
3import { createStore, applyMiddleware } from 'redux'
4import thunk from 'redux-thunk'
5
6import diff from 'virtual-dom/diff'
7import patch from 'virtual-dom/patch'
8import createElement from 'virtual-dom/create-element'
9
10import State from './State'
11import * as delegator from './dom/delegator'
12
13
14delegator.listen()
15
16const createStoreWithMiddleware = applyMiddleware(thunk)(createStore)
17
18
19export function start (component) {
20 const { init, update, view } = component
21 const [initialState, initialAction] = handleInit(init)
22
23 // Initial call to update() will be @@redux/INIT so bogus dispatch() is okay
24 let dispatch = x => x
25
26 const store = createStoreWithMiddleware((state = initialState, action) => {
27 const newState = update(state, action, dispatch)
28
29 return (typeof newState === 'undefined') ? state : newState
30 })
31
32 dispatch = makeDispatcher(store)
33
34 if (initialAction) {
35 store.dispatch(initialAction)
36 }
37
38 let tree = view(initialState, dispatch)
39 const rootNode = createElement(tree)
40
41 store.subscribe(() => {
42 tree = patchTree(
43 rootNode,
44 tree,
45 view(store.getState(), dispatch)
46 )
47 })
48
49 return rootNode
50}
51
52
53// Create a dispatcher function for the given store. Dispatchers act as a curried
54// interface to store.dispatch, allowing views to express the _intent to dispatch_
55// without immediately triggering a dispatch.
56function makeDispatcher (store) {
57 return action => {
58 return event => {
59 if (event) {
60 action.$event = event
61
62 if (action.$fwdAction) {
63 action.$fwdAction.$event = event
64 }
65 }
66
67 store.dispatch(action)
68 }
69 }
70}
71
72
73function patchTree (rootNode, oldTree, newTree) {
74 patch(rootNode, diff(oldTree, newTree))
75
76 return newTree
77}
78
79
80export function initializeComponent ({ init }, dispatch) {
81 const [initialState, initialAction] = handleInit(init)
82
83 if (dispatch && initialAction) {
84 dispatch(initialState)(initialAction)()
85 }
86
87 return initialState
88}
89
90
91function handleInit (init) {
92 const _res = init()
93 const res = Array.isArray(_res) ? _res : [_res]
94
95 return [new State(res[0]), res[1]]
96}
97
98
99// Wrap a dispatcher, forwarding any actions onto the specified action by attaching
100// them to the $fwdAction property.
101//
102// Usually used by parent components to capture actions from child components.
103export const forwardDispatch = curry((action, dispatch, state) => {
104 return forwardAction => {
105 if (typeof forwardAction === 'function') {
106 // In order to forward thunks, an intermediate thunk needs to be returned
107 // to gain access to the raw `action => <dispatch>` dispatcher rather than
108 // the application's wrapped `action => event => <dispatch>` dispatcher.
109 return dispatch(rawDispatch => {
110 const getState = () => state
111 const fwd = forwardDispatch(action, rawDispatch, state)
112
113 forwardAction(fwd, getState)
114 })
115 }
116
117 // Annotate and dispatch a simple action object
118 return dispatch(Object.assign({}, action, { $fwdAction: forwardAction }))
119 }
120})