UNPKG

10.9 kBJavaScriptView Raw
1var ActionTypes = {
2 INIT: "@@redux/INIT"
3};
4
5export function createStore(reducer, preloadedState, enhancer) {
6 if (typeof preloadedState === "function" && typeof enhancer === "undefined") {
7 enhancer = preloadedState;
8 preloadedState = undefined;
9 }
10
11 if (typeof enhancer !== "undefined") {
12 if (typeof enhancer !== "function") {
13 throw new Error("Expected the enhancer to be a function.");
14 }
15
16 return enhancer(createStore)(reducer, preloadedState);
17 }
18
19 if (typeof reducer !== "function") {
20 throw new Error("Expected the reducer to be a function.");
21 }
22
23 var currentReducer = reducer;
24 var currentState = preloadedState;
25 var currentListeners = [];
26 var nextListeners = currentListeners;
27 var isDispatching = false;
28
29 function ensureCanMutateNextListeners() {
30 if (nextListeners === currentListeners) {
31 nextListeners = currentListeners.slice();
32 }
33 }
34
35 function getState() {
36 return currentState;
37 }
38
39 function subscribe(listener) {
40 if (typeof listener !== "function") {
41 throw new Error("Expected listener to be a function.");
42 }
43
44 var isSubscribed = true;
45
46 ensureCanMutateNextListeners();
47 nextListeners.push(listener);
48
49 return function unsubscribe() {
50 if (!isSubscribed) {
51 return;
52 }
53
54 isSubscribed = false;
55
56 ensureCanMutateNextListeners();
57 var index = nextListeners.indexOf(listener);
58 nextListeners.splice(index, 1);
59 };
60 }
61
62 function dispatch(action) {
63 if (!isPlainObject(action)) {
64 throw new Error(
65 "Actions must be plain objects. " +
66 "Use custom middleware for async actions."
67 );
68 }
69
70 if (typeof action.type === "undefined") {
71 throw new Error(
72 'Actions may not have an undefined "type" property. ' +
73 "Have you misspelled a constant?"
74 );
75 }
76
77 if (isDispatching) {
78 throw new Error("Reducers may not dispatch actions.");
79 }
80
81 try {
82 isDispatching = true;
83 currentState = currentReducer(currentState, action);
84 } finally {
85 isDispatching = false;
86 }
87
88 var listeners = (currentListeners = nextListeners);
89 for (var i = 0; i < listeners.length; i++) {
90 var listener = listeners[i];
91 listener();
92 }
93
94 return action;
95 }
96
97 function replaceReducer(nextReducer) {
98 if (typeof nextReducer !== "function") {
99 throw new Error("Expected the nextReducer to be a function.");
100 }
101
102 currentReducer = nextReducer;
103 dispatch({ type: ActionTypes.INIT });
104 }
105
106 // When a store is created, an "INIT" action is dispatched so that every
107 // reducer returns their initial state. This effectively populates
108 // the initial state tree.
109 dispatch({ type: ActionTypes.INIT });
110
111 return {
112 dispatch: dispatch,
113 subscribe: subscribe,
114 getState: getState,
115 replaceReducer: replaceReducer
116 };
117}
118
119function warning(message) {
120 /* eslint-disable no-console */
121 if (typeof console !== "undefined" && typeof console.error === "function") {
122 console.error(message);
123 }
124 /* eslint-enable no-console */
125 try {
126 // This error was thrown as a convenience so that if you enable
127 // "break on all exceptions" in your console,
128 // it would pause the execution at this line.
129 throw new Error(message);
130 /* eslint-disable no-empty */
131 } catch (e) {}
132 /* eslint-enable no-empty */
133}
134
135function getUndefinedStateErrorMessage(key, action) {
136 var actionType = action && action.type;
137 var actionName =
138 (actionType && '"' + actionType.toString() + '"') || "an action";
139
140 return (
141 "Given action " +
142 actionName +
143 ', reducer "' +
144 key +
145 '" returned undefined. ' +
146 "To ignore an action, you must explicitly return the previous state. " +
147 "If you want this reducer to hold no value, you can return null instead of undefined."
148 );
149}
150
151function getUnexpectedStateShapeWarningMessage(
152 inputState,
153 reducers,
154 action,
155 unexpectedKeyCache
156) {
157 var reducerKeys = Object.keys(reducers);
158 var argumentName =
159 action && action.type === ActionTypes.INIT
160 ? "preloadedState argument passed to createStore"
161 : "previous state received by the reducer";
162
163 if (reducerKeys.length === 0) {
164 return (
165 "Store does not have a valid reducer. Make sure the argument passed " +
166 "to combineReducers is an object whose values are reducers."
167 );
168 }
169
170 if (!isPlainObject(inputState)) {
171 return (
172 "The " +
173 argumentName +
174 ' has unexpected type of "' +
175 {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
176 '". Expected argument to be an object with the following ' +
177 ('keys: "' + reducerKeys.join('", "') + '"')
178 );
179 }
180
181 var unexpectedKeys = Object.keys(inputState).filter(function(key) {
182 return !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key];
183 });
184
185 unexpectedKeys.forEach(function(key) {
186 unexpectedKeyCache[key] = true;
187 });
188
189 if (unexpectedKeys.length > 0) {
190 return (
191 "Unexpected " +
192 (unexpectedKeys.length > 1 ? "keys" : "key") +
193 " " +
194 ('"' +
195 unexpectedKeys.join('", "') +
196 '" found in ' +
197 argumentName +
198 ". ") +
199 "Expected to find one of the known reducer keys instead: " +
200 ('"' + reducerKeys.join('", "') + '". Unexpected keys will be ignored.')
201 );
202 }
203}
204
205function assertReducerShape(reducers) {
206 Object.keys(reducers).forEach(function(key) {
207 var reducer = reducers[key];
208 var initialState = reducer(undefined, { type: ActionTypes.INIT });
209
210 if (typeof initialState === "undefined") {
211 throw new Error(
212 'Reducer "' +
213 key +
214 '" returned undefined during initialization. ' +
215 "If the state passed to the reducer is undefined, you must " +
216 "explicitly return the initial state. The initial state may " +
217 "not be undefined. If you don't want to set a value for this reducer, " +
218 "you can use null instead of undefined."
219 );
220 }
221
222 var type =
223 "@@redux/PROBE_UNKNOWN_ACTION_" +
224 Math.random()
225 .toString(36)
226 .substring(7)
227 .split("")
228 .join(".");
229 if (typeof reducer(undefined, { type: type }) === "undefined") {
230 throw new Error(
231 'Reducer "' +
232 key +
233 '" returned undefined when probed with a random type. ' +
234 ("Don't try to handle " +
235 ActionTypes.INIT +
236 ' or other actions in "redux/*" ') +
237 "namespace. They are considered private. Instead, you must return the " +
238 "current state for any unknown actions, unless it is undefined, " +
239 "in which case you must return the initial state, regardless of the " +
240 "action type. The initial state may not be undefined, but can be null."
241 );
242 }
243 });
244}
245
246export function combineReducers(reducers) {
247 var reducerKeys = Object.keys(reducers);
248 var finalReducers = {};
249 for (var i = 0; i < reducerKeys.length; i++) {
250 var key = reducerKeys[i];
251
252 {
253 if (typeof reducers[key] === "undefined") {
254 warning('No reducer provided for key "' + key + '"');
255 }
256 }
257
258 if (typeof reducers[key] === "function") {
259 finalReducers[key] = reducers[key];
260 }
261 }
262 var finalReducerKeys = Object.keys(finalReducers);
263
264 var unexpectedKeyCache = void 0;
265 {
266 unexpectedKeyCache = {};
267 }
268
269 var shapeAssertionError = void 0;
270 try {
271 assertReducerShape(finalReducers);
272 } catch (e) {
273 shapeAssertionError = e;
274 }
275
276 return function combination() {
277 var state =
278 arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
279 var action = arguments[1];
280
281 if (shapeAssertionError) {
282 throw shapeAssertionError;
283 }
284
285 {
286 var warningMessage = getUnexpectedStateShapeWarningMessage(
287 state,
288 finalReducers,
289 action,
290 unexpectedKeyCache
291 );
292 if (warningMessage) {
293 warning(warningMessage);
294 }
295 }
296
297 var hasChanged = false;
298 var nextState = {};
299 for (var _i = 0; _i < finalReducerKeys.length; _i++) {
300 var _key = finalReducerKeys[_i];
301 var reducer = finalReducers[_key];
302 var previousStateForKey = state[_key];
303 var nextStateForKey = reducer(previousStateForKey, action);
304 if (typeof nextStateForKey === "undefined") {
305 var errorMessage = getUndefinedStateErrorMessage(_key, action);
306 throw new Error(errorMessage);
307 }
308 nextState[_key] = nextStateForKey;
309 hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
310 }
311 return hasChanged ? nextState : state;
312 };
313}
314
315function bindActionCreator(actionCreator, dispatch) {
316 return function() {
317 return dispatch(actionCreator.apply(undefined, arguments));
318 };
319}
320
321export function bindActionCreators(actionCreators, dispatch) {
322 if (typeof actionCreators === "function") {
323 return bindActionCreator(actionCreators, dispatch);
324 }
325
326 if (typeof actionCreators !== "object" || actionCreators === null) {
327 throw new Error(
328 "bindActionCreators expected an object or a function, instead received " +
329 (actionCreators === null ? "null" : typeof actionCreators) +
330 ". " +
331 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?'
332 );
333 }
334
335 var keys = Object.keys(actionCreators);
336 var boundActionCreators = {};
337 for (var i = 0; i < keys.length; i++) {
338 var key = keys[i];
339 var actionCreator = actionCreators[key];
340 if (typeof actionCreator === "function") {
341 boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
342 }
343 }
344 return boundActionCreators;
345}
346
347export function compose() {
348 for (
349 var _len = arguments.length, funcs = Array(_len), _key = 0;
350 _key < _len;
351 _key++
352 ) {
353 funcs[_key] = arguments[_key];
354 }
355
356 if (funcs.length === 0) {
357 return function(arg) {
358 return arg;
359 };
360 }
361
362 if (funcs.length === 1) {
363 return funcs[0];
364 }
365
366 return funcs.reduce(function(a, b) {
367 return function() {
368 return a(b.apply(undefined, arguments));
369 };
370 });
371}
372
373export function applyMiddleware() {
374 for (
375 var _len = arguments.length, middlewares = Array(_len), _key = 0;
376 _key < _len;
377 _key++
378 ) {
379 middlewares[_key] = arguments[_key];
380 }
381
382 return function(createStore) {
383 return function(reducer, preloadedState, enhancer) {
384 var store = createStore(reducer, preloadedState, enhancer);
385 var _dispatch = store.dispatch;
386 var chain = [];
387
388 var middlewareAPI = {
389 getState: store.getState,
390 dispatch: function dispatch(action) {
391 return _dispatch(action);
392 }
393 };
394 chain = middlewares.map(function(middleware) {
395 return middleware(middlewareAPI);
396 });
397 _dispatch = compose.apply(undefined, chain)(store.dispatch);
398
399 return Object.assign({}, store, {
400 dispatch: _dispatch
401 });
402 };
403 };
404}