/** * These are private action types reserved by Redux. * For any unknown actions, you must return the current state. * If the current state is undefined, you must return the initial state. * Do not reference these action types directly in your code. */ // 初始化的时候(redux.createStore(reducer, initialState)时),传的action.type 就是这货啦 exportvar ActionTypes = { INIT: '@@redux/INIT' };
/** * Creates a Redux store that holds the state tree. * The only way to change the data in the store is to call `dispatch()` on it. * * There should only be a single store in your app. To specify how different * parts of the state tree respond to actions, you may combine several reducers * into a single reducer function by using `combineReducers`. * * @param {Function} reducer A function that returns the next state tree, given * the current state tree and the action to handle. * * @param {any} [initialState] The initial state. You may optionally specify it * to hydrate the state from the server in universal apps, or to restore a * previously serialized user session. * If you use `combineReducers` to produce the root reducer function, this must be * an object with the same shape as `combineReducers` keys. * * @returns {Store} A Redux store that lets you read the state, dispatch actions * and subscribe to changes. */ exportdefaultfunctioncreateStore(reducer, initialState) { if (typeof reducer !== 'function') { thrownewError('Expected the reducer to be a function.'); }
var currentReducer = reducer; var currentState = initialState; var listeners = []; var isDispatching = false;
/** * Reads the state tree managed by the store. * * @returns {any} The current state tree of your application. */ // 这个方法没什么好讲的,返回当前的state functiongetState() { return currentState; }
/** * Adds a change listener. It will be called any time an action is dispatched, * and some part of the state tree may potentially have changed. You may then * call `getState()` to read the current state tree inside the callback. * * @param {Function} listener A callback to be invoked on every dispatch. * @returns {Function} A function to remove this change listener. */ // 很常见的监听函数添加方式,当store.dispatch 的时候被调用 // store.subscribe(listener) 返回一个方法(unscribe),可以用来取消监听 functionsubscribe(listener) { listeners.push(listener); var isSubscribed = true;
returnfunctionunsubscribe() { if (!isSubscribed) { return; }
isSubscribed = false; var index = listeners.indexOf(listener); listeners.splice(index, 1); }; }
/** * Dispatches an action. It is the only way to trigger a state change. * * The `reducer` function, used to create the store, will be called with the * current state tree and the given `action`. Its return value will * be considered the **next** state of the tree, and the change listeners * will be notified. * * The base implementation only supports plain object actions. If you want to * dispatch a Promise, an Observable, a thunk, or something else, you need to * wrap your store creating function into the corresponding middleware. For * example, see the documentation for the `redux-thunk` package. Even the * middleware will eventually dispatch plain object actions using this method. * * @param {Object} action A plain object representing “what changed”. It is * a good idea to keep actions serializable so you can record and replay user * sessions, or use the time travelling `redux-devtools`. An action must have * a `type` property which may not be `undefined`. It is a good idea to use * string constants for action types. * * @returns {Object} For convenience, the same action object you dispatched. * * Note that, if you use a custom middleware, it may wrap `dispatch()` to * return something else (for example, a Promise you can await). */ // 以下情况会报错 // 1. 传入的action不是一个对象 // 2. 传入的action是个对象,但是action.type 是undefined functiondispatch(action) { if (!isPlainObject(action)) { thrownewError( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ); }
if (typeof action.type === 'undefined') { thrownewError( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ); }
if (isDispatching) { thrownewError('Reducers may not dispatch actions.'); }
/** * Replaces the reducer currently used by the store to calculate the state. * * You might need this if your app implements code splitting and you want to * load some of the reducers dynamically. You might also need this if you * implement a hot reloading mechanism for Redux. * * @param {Function} nextReducer The reducer for the store to use instead. * @returns {void} */ functionreplaceReducer(nextReducer) { currentReducer = nextReducer; dispatch({ type: ActionTypes.INIT }); }
// When a store is created, an "INIT" action is dispatched so that every // reducer returns their initial state. This effectively populates // the initial state tree. // // redux.createStore(reducer, initialState) 的时候, 内部会 自己调用 dispatch({ type: ActionTypes.INIT }); // 来完成state的初始化 dispatch({ type: ActionTypes.INIT });
functiongetUndefinedStateErrorMessage(key, action) { var actionType = action && action.type; var actionName = actionType && `"${actionType.toString()}"` || 'an action';
return ( `Reducer "${key}" returned undefined handling ${actionName}. ` + `To ignore an action, you must explicitly return the previous state.` ); }
functiongetUnexpectedStateKeyWarningMessage(inputState, outputState, action) { var reducerKeys = Object.keys(outputState); var argumentName = action && action.type === ActionTypes.INIT ? 'initialState argument passed to createStore' : 'previous state received by the reducer';
if (reducerKeys.length === 0) { return ( 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.' ); }
if (!isPlainObject(inputState)) { return ( `The ${argumentName} has unexpected type of "` + ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + `". Expected argument to be an object with the following ` + `keys: "${reducerKeys.join('", "')}"` ); }
var unexpectedKeys = Object.keys(inputState).filter( key => reducerKeys.indexOf(key) < 0 );
if (unexpectedKeys.length > 0) { return ( `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` + `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` + `Expected to find one of the known reducer keys instead: ` + `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.` ); } }
if (typeof initialState === 'undefined') { thrownewError( `Reducer "${key}" returned undefined during initialization. ` + `If the state passed to the reducer is undefined, you must ` + `explicitly return the initial state. The initial state may ` + `not be undefined.` ); }
var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.'); if (typeof reducer(undefined, { type }) === 'undefined') { thrownewError( `Reducer "${key}" returned undefined when probed with a random type. ` + `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + `namespace. They are considered private. Instead, you must return the ` + `current state for any unknown actions, unless it is undefined, ` + `in which case you must return the initial state, regardless of the ` + `action type. The initial state may not be undefined.` ); } }); }
/** * Turns an object whose values are different reducer functions, into a single * reducer function. It will call every child reducer, and gather their results * into a single state object, whose keys correspond to the keys of the passed * reducer functions. * * @param {Object} reducers An object whose values correspond to different * reducer functions that need to be combined into one. One handy way to obtain * it is to use ES6 `import * as reducers` syntax. The reducers may never return * undefined for any action. Instead, they should return their initial state * if the state passed to them was undefined, and the current state for any * unrecognized action. * * @returns {Function} A reducer function that invokes every reducer inside the * passed object, and builds a state object with the same shape. */
exportdefaultfunctioncombineReducers(reducers) { // 返回一个对象, key => value 且value是function(其实就是过滤掉非function) var finalReducers = pick(reducers, (val) => typeof val === 'function'); var sanityError;
/** * Turns an object whose values are action creators, into an object with the * same keys, but with every function wrapped into a `dispatch` call so they * may be invoked directly. This is just a convenience method, as you can call * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. * * For convenience, you can also pass a single function as the first argument, * and get a function in return. * * @param {Function|Object} actionCreators An object whose values are action * creator functions. One handy way to obtain it is to use ES6 `import * as` * syntax. You may also pass a single function. * * @param {Function} dispatch The `dispatch` function available on your Redux * store. * * @returns {Function|Object} The object mimicking the original object, but with * every action creator wrapped into the `dispatch` call. If you passed a * function as `actionCreators`, the return value will also be a single * function. */ // 假设 actionCreators === {addTodo: addTodo, removeTodo: removeTodo} // 简单的来说 bindActionCreators(actionCreators, dispatch) // 最后返回的是: // { // addTodo: function(text){ // dispatch( actionCreators.addTodo(text) ); // }, // removeTodo: function(text){ // dispatch( actionCreators.removeTodo(text) ); // } // } // // 或者说 actionCreators === addTodo (addTodo 为 actionCreator) // 最后返回的是 // function() { // dispatch(actionCreators()); // } exportdefaultfunctionbindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch); }
if (typeof actionCreators !== 'object' || actionCreators === null || actionCreators === undefined) { // eslint-disable-line no-eq-null thrownewError( `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` + `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` ); }
/** * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks, such as expressing * asynchronous actions in a concise manner, or logging every action payload. * * See `redux-thunk` package as an example of the Redux middleware. * * Because middleware is potentially asynchronous, this should be the first * store enhancer in the composition chain. * * Note that each middleware will be given the `dispatch` and `getState` functions * as named arguments. * * @param {...Function} middlewares The middleware chain to be applied. * @returns {Function} A store enhancer applying the middleware. */ /* 从调用方法 applyMiddleware(...middlewares)(Redux.createStore) 可以看出 next 参数实际上是 Redux.createStore. 而 Redux.createStore 的调用方式为 Redux.createStore(reducer, initialState) 所以 applyMiddleware(...middlewares) 1. 参数: Redux.createStore 2. 返回值:一个function, 跟 Redux.createStore 接受的参数一样 */ exportdefaultfunctionapplyMiddleware(...middlewares) { return(next) => (reducer, initialState) => { // 内部先创建一个store (相当于直接调用 Redux.createStore(reducer, initialState)) var store = next(reducer, initialState); // 保存最初始的store.dispatch var dispatch = store.dispatch; var chain = [];