UNPKG

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