-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsubstateMultiplexer.js
More file actions
175 lines (154 loc) · 4.77 KB
/
Copy pathsubstateMultiplexer.js
File metadata and controls
175 lines (154 loc) · 4.77 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
169
170
171
172
173
174
175
// @flow
import { combineReducers } from 'redux';
import type { ID_TYPE } from './types';
import type { OrderActionType } from './order';
import commonById from './byId';
import commonOrder from './order';
import commonSelected from './selected';
type SubstateMultiplexerConfigurationType = {
added?: Array<string>,
fetched?: Array<string>,
removed?: Array<string>,
cleared?: Array<string>,
replaced?: Array<string>,
confirmed?: Array<string>,
sorted?: Array<string>,
preferPrepend?: boolean,
allDeselected?: Array<string>,
selected?: Array<string>,
rehydrated?: Array<string>,
idKey?: string,
reducer: Function,
}
type SubstateMultiplexerActionType = OrderActionType;
export type SubstateMultiplexerStateType = {
byId: {[ID_TYPE]: Object},
order: Array<ID_TYPE>,
selected: ?ID_TYPE,
substates: Object,
};
const initialState = {
byId: {},
order: [],
selected: null,
substates: {},
};
const substateMultiplexer = (configuration: SubstateMultiplexerConfigurationType) => {
const byIdOrderAndSelectedReducer = combineReducers({
byId: commonById({
added: configuration.added,
fetched: configuration.fetched,
removed: configuration.removed,
cleared: configuration.cleared,
idKey: configuration.idKey,
}),
order: commonOrder({
added: configuration.added,
fetched: configuration.fetched,
replaced: configuration.replaced,
removed: configuration.removed,
confirmed: configuration.confirmed,
cleared: configuration.cleared,
sorted: configuration.sorted,
idKey: configuration.idKey,
preferPrepend: configuration.preferPrepend,
}),
selected: commonSelected({
selected: configuration.selected,
allDeselected: configuration.allDeselected,
default: null,
}),
});
return (
state: SubstateMultiplexerStateType = initialState,
action: SubstateMultiplexerActionType,
): SubstateMultiplexerStateType => {
// Initial run of the reducer needs to return the reference to the initial state
if (configuration.rehydrated && configuration.rehydrated.includes(action.type)) {
return state;
}
const { substates } = state;
const newSubstates = { ...substates };
const byIdOrderAndSelected = byIdOrderAndSelectedReducer(
{
byId: state.byId,
order: state.order,
selected: state.selected,
},
action,
);
const { byId, order } = byIdOrderAndSelected;
let { selected } = byIdOrderAndSelected;
// Select the first one if just added one and there was anything selected
if (
(
(configuration.added && configuration.added.includes(action.type))
|| (configuration.fetched && configuration.fetched.includes(action.type))
)
&& order.length > 0
&& selected === null
) {
selected = order[0]; // eslint-disable-line prefer-destructuring
}
// Remove substate
if (configuration.removed && configuration.removed.includes(action.type)) {
const { payload } = action;
if (typeof payload === 'number' || typeof payload === 'string') {
delete newSubstates[payload];
}
}
// Re-select if removed the one that is currently selected
if (
configuration.removed
&& configuration.removed.includes(action.type)
&& selected !== null
&& !order.includes(selected)
) {
// If there are another options, select the first one
if (order.length > 0) {
selected = order[0]; // eslint-disable-line prefer-destructuring
// Mark that nothing is selected
} else {
selected = null;
}
}
return {
byId,
order,
selected,
substates: selected != null ? {
...newSubstates,
[selected]: configuration.reducer(newSubstates[selected], action),
} : newSubstates,
};
};
};
export default substateMultiplexer;
export const reselectWithMultiplexer = (selector: Function): Function => (multiplexerState: SubstateMultiplexerStateType, ...args: Array<mixed>) => {
const { selected, substates } = multiplexerState;
if (selected != null) {
if (substates[selected] != null) {
return selector(substates[selected], ...args);
} else {
throw new Error('Invalid selected substate');
}
} else {
throw new Error('No substate is selected');
}
};
export const multipleReselectsWithMultiplexer = ({
selectors = {},
excluded = [],
}: {
selectors: {[string]: Function},
excluded?: Array<string>,
}): {[string]: Function} => {
const wSelectors = {};
Object.keys(selectors).filter(
selectorName => selectorName !== 'default'
&& !excluded.includes(selectorName),
).forEach((selectorName) => {
wSelectors[selectorName] = reselectWithMultiplexer(selectors[selectorName]);
});
return wSelectors;
};