forked from ehmicky/Notes
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathflux.javascript.txt
More file actions
168 lines (145 loc) · 11 KB
/
flux.javascript.txt
File metadata and controls
168 lines (145 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
┏━━━━━━━━━━┓
┃ FLUX ┃
┗━━━━━━━━━━┛
COMPARISON ==> #Flux:
# - original one
# - orchestration: use dispatcher
# - stores listens to specific dispatchers
# - stores change state with reducer function
#Redux:
# - most popular one, rich ecosystem
# - orchestration: use custom imperative code
# - stores listens to all actions, and whitelist them
# - stores change state with reducer function
# - encourage single top-level store, with substores
# - views can change which stores they listen to
#Reflux:
# - orchestration: use helper listener functions
# - stores listens to specific actions
# - stores change state with imperative code
# - does not separate store states and view states
# - views are updated even if stores set state to same state
#Prefer Redux
VERSION ==> #2.1.1
DEVELOPMENT MODE ==> #Like React
FLOW ==> #Specific case of MVC architecture.
#Design:
# - steps:
# - one 'action', i.e. view event (i.e. anything changing state) notifying all models
# - if event relevant, models update
# - then whole view tree re-rendered with new models state
# - abstract APPSTATE/ACTION as a layer set by views, get by models:
# - views do not update models directly, they update APPSTATE
# - one-way data binding: always models -> views
#Implementation:
# - Views notify singleton Dispatcher that app state changed
# - Dispatcher triggers all Stores __onDispatch() listener
# - Dispatcher's goal is to manage dependencies (see e.g. DISPATCHER.waitFor())
# - Stores __onDispatch() do model logic, optionally notifying they changed
# - are one or several models, associated to a common purpose/domain
# - Views associated with a Store that changed are re-rendered:
# - those Views should be only few (e.g. only one root) because:
# - if two Views are associated with same Store and they have common children,
# children will be re-rendered twice, with potentially different props|state
# - with two-way data binding, this cannot be solved easily
# - are called "Controller-Views" (see React documentation about 'smart' components)
# - can pass all the Stores state to descendants as props, to simplify code,
# or reduce/transform/pick it ("selectors")
#Steps (+ are done once, - at each dispatch)
# + DISPATCHER = new Dispatcher()
# - should be an app singleton
# - DISPATCHER.dispatch(ACTION):
# - notify new app state
# + STORE = new FluxStore(DISPATCHER)
# - calls DISPATCHER.register(), i.e. DISPATCHER.dispatch(ACTION) will fire STORE.__onDispatch(ACTION)
# - STORE.__onDispatch(ACTION)
# - do model logic according to ACTION
# - STORE.__emitChange() if model has changed
# + STORE.addListener(SFUNC()):
# - fire SFUNC() on STORE.__emitChange()
# + Ftype.getStores():
# - associate view with stores
# - Ftype.calculateState():
# - update view when stores change, i.e. on SFUNC()
┌────────────┐
│ ACTION │
└────────────┘
ACTION ==> # - on events, e.g. user interaction in views
# - for async actions, should have at least three actions:
# - when registering events
# - on event success
# - on event failure
# - can be abstracted by an interface ("action creator"):
# - calling relevant DISPATCHER.dispatch(ACTION)
# - or 'pure functions' just returning ACTION
# - ACTION should be a self-contained command object:
# - ACTION.type: so that STORE can know if it's relevant for them
# - enough information for the action to be performed
FLUX STANDARD ACTION ==> ##Convention on what an ACTION should be (version 1.1.0)
##Cannot include anything else than the following members
ACTION.type ##STR|SYM
ACTION.payload ##VAL
ACTION.error ##BOOL. If true, ACTION.payload must be ERROR
ACTION.meta ##VAL
FSA.isFSA(ACTION)->BOOL ##
FSA.isError(ACTION)->BOOL ##
┌────────────────┐
│ DISPATCHER │
└────────────────┘
new Dispatcher() #DISPATCHER
DISPTCHR.register(DFUNC)->DFUNC_ID#
DISPATCHER.unregister(DFUNC_ID) #
DISPATCHER.dispatch(ACTION) #Fires each DFUNC(ACTION)
#Cannot be done if on-going DISPATCHER.dispatch()
DISPATCHER.isDispatching()->BOOL #
DISPATCHER.waitFor(DFUNC_ID_ARR) #To be fired while DISPATCHER.isDispatching(), i.e. in a DFUNC2.
#Fire those DFUNC right away (unless already fired), to make sure they are fired first.
┌───────────┐
│ STORE │
└───────────┘
new FluxStore(DISPATCHER) #STORE
#Calls DISPATCHER.register(STORE.__onDispatch(ACTION))
STORE.__onDispatch(ACTION) #
STORE.getDispatcher()->DISPATCHER #
STORE.getDispatchToken()->DFUNC_ID#
STORE.addListener(SFUNC()) #Returns OBJ: remove()
STORE.__emitChange() #Fires SFUNC(), providing called within STORE.__onDispatch(), itself called through DISPATCHER.
STORE.hasChanged()->BOOL #Has __emitChange() been called by last __onDispatch()
new FluxStoreGroup #FLUXSTOREGROUP
(STORE_ARR, FUNC()) #All STORE must have the same DISPATCHER
#When DISPATCHER.dispatch() is called, fires FUNC() when all STORE.__onDispatch(ACTION) have been called.
FLUXSTOREGROUP.release() #Cleanup
new FluxReduceStore(DISPATCHER) #REDUCESTORE, child of STORE, with a state object.
REDUCESTORE.getInitialState()->OBJ#Called at construction, must be overriden.
REDUCESTORE.getState()->OBJ #
REDUCESTORE.__onDispatch(ACTION) #Overriden to call REDUCESTORE.reduce(APPSTATE, ACTION)->APPSTATE2
REDUCESTORE.reduce #Does:
(APPSTATE, ACTION)->APPSTATE2 # - APPSTATE2 is new APPSTATE, and cannot be undefined
# - calls __emitChange() if APPSTATE changed
# - checked using REDUCESTORE.areEqual(APPSTATE, APPSTATE2) (def: ===)
# - should be pure, i.e.:
# - in order to be keep track of each successive APPSTATE (e.g. for debugging),
# and easier check of APPSTATE change (using === instead of deep comparison):
# - APPSTATE2 should not be a reference to APPSTATE
# - APPSTATE|ACTION should be read-only (e.g. using Immutable.js)
# - in order to be repeatable:
# - should not modify global state nor arguments
# - should not read global state
┌──────────┐
│ VIEW │
└──────────┘
APPSTATE #State used by views, deduced (but different) from stores state
FluxContainer.create(RTYPE[, OPT])#Return FTYPE, i.e. child of RTYPE, which:
# - associate RTYPE with STOREs
# - updates it after a dispatch if any STORE.hasChanged()
#OPT:
# - withProps|Context BOOL (def: false): pass PROPS|CONTEXT to following functions
# - pure BOOL (def: true): make FTYPE.shouldComponentUpdate() use REACT.PureComponent
Ftype.getStores([PROPS[,CONTEXT]])#Associated STORE. Must all have same DISPATCHER
->STORE_ARR #Fired on PROPS|CONTEXT change if OPT.withProps|Context true
Ftype.calculateState #Does FTYPE.state = NEW_APPSTATE
(PREV_APPSTATE[, PROPS[,CONTEXT]])# - at initialization (PREV_APPSTATE is null)
->NEW_APPSTATE # - each time a DISPATCHER.dispatch() ends, if a STORE.hasChanged()
# - on componentWillReceiveProps, if OPT.withProps|Context true
FluxContainer.createFunctional #Same as FluxContainer.create() but for stateless components.
(RTYPE, FUNC, FUNC2[, OPT]) #FUNC is getStores(), FUNC2 calculateState()