一、以下总览: 1、状态管理:setstate--新旧值比较-- - render-- - 页面重绘,redux.js单项数据流,react-redux.js状态管理 2、页面初始化时,触发了dispatch--currentReducer--reducer--rootReducer,进而是state由空变为非空,与enhancer无关。 3、状态获取 function createStore(reducer, preloadedState, enhancer) { return enhancer(createStore)(reducer, preloadedState); } //麻痹的,为了获得最终的状态管理对象,需要经过4轮连锁执行 createStore( rootReducer, preloadedState, compose(functionA,functionB,functionC,functionD)//enhancer ) //柯里化函数:接受最初函数的第一个参数,用返回值函数接受余下的参数,再返回最终结果的函数。 //柯里化函数:functionA,functionB,functionC,functionD。 function functionA(createStore3) { return function createStore4(reducer4, preloadedState4, enhancer4) {//2、实际执行createStore4(reducer, preloadedState), var store3=createStore3(reducer3, preloadedState3, enhancer3);//3、其上加工参数,其下加工返回值,最后返回最终结果 return { dispatch: dispatch3, subscribe: subscribe3, getState: getState3, replaceReducer: replaceReducer3 } } }; function functionB(createStore2) { return function createStore3(reducer3, preloadedState3, enhancer3) { var store2=createStore2(reducer2, preloadedState2, enhancer2); return { dispatch: dispatch2, subscribe: subscribe2, getState: getState2, replaceReducer: replaceReducer2 } } }; function functionC(createStore1) { return function createStore2(reducer2, preloadedState2, enhancer2) { var store1=createStore1(reducer1, preloadedState1, enhancer1); return { dispatch: dispatch1, subscribe: subscribe1, getState: getState1, replaceReducer: replaceReducer1 } } }; function functionD(createStore0) {//1、实际执行functionD(createStore),逐个向上返回 return function createStore1(reducer1, preloadedState1, enhancer1) { var store0=createStore0(reducer0, preloadedState0, enhancer0); //4、对返回值store0进行加工,逐个向上返回 return { dispatch: dispatch0, subscribe: subscribe0, getState: getState0, replaceReducer: replaceReducer0 } } }; (1)compose通过reduce函数引发第1次连锁执行,对functionA、functionB、functionC、functionD进行层层包裹,最后的结果作为enhancer。 (2)createStore把自身作为实参传给enhancer执行,即enhancer(createStore),即functionD(createStore),返回createStore1...,引发第2次连锁执行,最后返回createStore4。 (3)createStore把自身接受到的前两个参数reducer和preloadedState传给createStore4,即createStore4(reducer,preloadedState),进而执行createStore3...,引发第3次连锁执行,最后return时,又引发了第4次连锁执行。 3、compose执行 var arrayFunction = [functionA, functionB, functionC, functionD]; function compose(arrayFunction) { return arrayFunction.reduce(function (a, b) { return function () {//此处的return的必要性,曾经存疑 return a(b.apply(undefined, arguments)); }; }) } compose(arrayFunction)(createStore)(reducer, preloadedState); //第一次循环后的结果如下 function functionOne() { return functionA(functionB.apply(undefined, arguments)); } //第二次循环后的结果如下 function functionTwo() { return functionOne(functionC.apply(undefined, arguments)); } //第三次循环后的结果如下 function functionThree() { return functionTwo(functionD.apply(undefined, arguments)); } //functionThree实际上就是enhancer,通过functionThree(createStore)(reducer, preloadedState)起作用。 二、以下实际应用: import { createStore, applyMiddleware, compose } from ‘redux‘; import thunkMiddleware from ‘redux-thunk‘; import rootReducer from ‘reducers/index‘; import DevTools from ‘containers/DevTools‘; export default function configureStore(preloadedState) { const store = createStore( rootReducer, preloadedState, compose( applyMiddleware(thunkMiddleware), DevTools.instrument() ) ) return store } function createStore(reducer, preloadedState, enhancer) { var _ref2; if (typeof preloadedState === ‘function‘ && typeof enhancer === ‘undefined‘) { enhancer = preloadedState; preloadedState = undefined; } if (typeof enhancer !== ‘undefined‘) { if (typeof enhancer !== ‘function‘) { throw new Error(‘Expected the enhancer to be a function.‘); } return enhancer(createStore)(reducer, preloadedState);//////////////////////// } if (typeof reducer !== ‘function‘) { throw new Error(‘Expected the reducer to be a function.‘); } var currentReducer = reducer; var currentState = preloadedState; var currentListeners = []; var nextListeners = currentListeners; var isDispatching = false; function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice(); } } function getState() { return currentState; } function subscribe(listener) { if (typeof listener !== ‘function‘) { throw new Error(‘Expected listener to be a function.‘); } var isSubscribed = true; ensureCanMutateNextListeners(); nextListeners.push(listener); return function unsubscribe() { if (!isSubscribed) { return; } isSubscribed = false; ensureCanMutateNextListeners(); var index = nextListeners.indexOf(listener); nextListeners.splice(index, 1); }; } function dispatch(action) { if (!isPlainObject(action)) { throw new Error(‘Actions must be plain objects. ‘ + ‘Use custom middleware for async actions.‘); } if (typeof action.type === ‘undefined‘) { throw new Error(‘Actions may not have an undefined "type" property. ‘ + ‘Have you misspelled a constant?‘); } if (isDispatching) { throw new Error(‘Reducers may not dispatch actions.‘); } try { isDispatching = true; currentState = currentReducer(currentState, action); } finally { isDispatching = false; } var listeners = currentListeners = nextListeners; for (var i = 0; i < listeners.length; i++) { var listener = listeners[i]; listener(); } return action; } function replaceReducer(nextReducer) { if (typeof nextReducer !== ‘function‘) { throw new Error(‘Expected the nextReducer to be a function.‘); } currentReducer = nextReducer; dispatch({ type: ActionTypes.INIT }); } function observable() { var _ref; var outerSubscribe = subscribe; return _ref = { subscribe: function subscribe(observer) { if (typeof observer !== ‘object‘) { throw new TypeError(‘Expected the observer to be an object.‘); } function observeState() { if (observer.next) { observer.next(getState()); } } observeState(); var unsubscribe = outerSubscribe(observeState); return { unsubscribe: unsubscribe }; } }, _ref[result] = function () { return this; }, _ref; } dispatch({ type: ActionTypes.INIT }); return _ref2 = { dispatch: dispatch, subscribe: subscribe, getState: getState, replaceReducer: replaceReducer }, _ref2[result] = observable, _ref2; } function compose() { for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) { funcs[_key] = arguments[_key]; } if (funcs.length === 0) { return function (arg) { return arg; }; } if (funcs.length === 1) { return funcs[0]; } return funcs.reduce(function (a, b) { return function () { return a(b.apply(undefined, arguments)); }; }); } function applyMiddleware() {//这是一个加工dispatch的中间件 for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) { middlewares[_key] = arguments[_key]; } return function (createStore) { return function (reducer, preloadedState, enhancer) { var store = createStore(reducer, preloadedState, enhancer); var _dispatch = store.dispatch; var chain = []; var middlewareAPI = { getState: store.getState, dispatch: function dispatch(action) {//此处为什么不是dispatch:store.dispatch return _dispatch(action); } }; chain = middlewares.map(function (middleware) { return middleware(middlewareAPI);//return thunkMiddleware(_ref) }); _dispatch = compose.apply(undefined, chain)(store.dispatch);//_dispatch = (function (next){})(store.dispatch) ,这是dispatch的新定义。 return _extends({}, store, {//store里的dispatch,被这里的dispatch覆盖 dispatch: _dispatch }); }; }; } function combineReducers(reducers) { var reducerKeys = Object.keys(reducers); var finalReducers = {}; for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i]; { if (typeof reducers[key] === ‘undefined‘) { warning(‘No reducer provided for key "‘ + key + ‘"‘); } } if (typeof reducers[key] === ‘function‘) { finalReducers[key] = reducers[key]; } } var finalReducerKeys = Object.keys(finalReducers); var unexpectedKeyCache = void 0; { unexpectedKeyCache = {}; } var shapeAssertionError = void 0; try { assertReducerShape(finalReducers); } catch (e) { shapeAssertionError = e; } return function combination() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var action = arguments[1]; if (shapeAssertionError) { throw shapeAssertionError; } { var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache); if (warningMessage) { warning(warningMessage); } } var hasChanged = false; var nextState = {}; for (var _i = 0; _i < finalReducerKeys.length; _i++) { var _key = finalReducerKeys[_i]; var reducer = finalReducers[_key]; var previousStateForKey = state[_key];//previousStateForKey可能为undefined var nextStateForKey = reducer(previousStateForKey, action);//可能执行reducer(undefined,action),即执行reducer(defaultState,action)。 if (typeof nextStateForKey === ‘undefined‘) { var errorMessage = getUndefinedStateErrorMessage(_key, action); throw new Error(errorMessage); } nextState[_key] = nextStateForKey; hasChanged = hasChanged || nextStateForKey !== previousStateForKey; // const defaultState = { // loading: true, // }; // export default function accountReducer(state = defaultState, action) { // switch (action.type) { // case "register_loading_false": // return {...state, loading: false } // //action匹配成功,state的引用就改变了 // default: // return state; // //action匹配不成功,state的引用就不改变了 // } // } } //遍历结束,一次性返回结果。 return hasChanged ? nextState : state; }; } function thunkMiddleware(_ref) {//middleware(middlewareAPI) var dispatch = _ref.dispatch; var getState = _ref.getState; return function (next) {//即return function(store.dispatch);next是dispatch的旧定义,即applyMiddleware函数的chain数组的下一项的执行结果。 return function (action) {//返回的函数是dispatch的新定义;其中action是参数,原本只能是对象,经改造后还可以是函数。 if (typeof action === ‘function‘) { return action(dispatch, getState);//把旧的dispatch、getState传进自定义函数参数里 } return next(action); }; }; } function componentWillMount() { const current = [ { location: "222", url: "/" }, { location: "333", url: "/carDetail" }, ]; this.props.dispatch(menuChange(current)) } export const menuChange = function (key) { return function (dispatch) { dispatch({ type: "menu_change", key }) } } function instrument() { var monitorReducer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () { return null; }; var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (typeof options.maxAge === ‘number‘ && options.maxAge < 2) { throw new Error(‘DevTools.instrument({ maxAge }) option, if specified, ‘ + ‘may not be less than 2.‘); } return function (createStore) { return function (reducer, initialState, enhancer) { function liftReducer(r) { if (typeof r !== ‘function‘) { if (r && typeof r.default === ‘function‘) { throw new Error(‘Expected the reducer to be a function. ‘ + ‘Instead got an object with a "default" field. ‘ + ‘Did you pass a module instead of the default export? ‘ + ‘Try passing require(...).default instead.‘); } throw new Error(‘Expected the reducer to be a function.‘); } return liftReducerWith(r, initialState, monitorReducer, options); } var liftedStore = createStore(liftReducer(reducer), enhancer); if (liftedStore.liftedStore) { throw new Error(‘DevTools instrumentation should not be applied more than once. ‘ + ‘Check your store configuration.‘); } return unliftStore(liftedStore, liftReducer, options); }; }; } 三、附源码 //附:thunk.js (function webpackUniversalModuleDefinition(root, factory) { if (typeof exports === ‘object‘ && typeof module === ‘object‘) module.exports = factory();//common.js模块下执行,factory()的执行结果为null else if (typeof define === ‘function‘ && define.amd) define([], factory);//require.js异步模块下执行 else if (typeof exports === ‘object‘) exports["ReduxThunk"] = factory();//ES6模块下执行,factory()的执行结果为null else root["ReduxThunk"] = factory();//非模块下执行,factory()的执行结果为null })( this, function () { return (function (modules) { var installedModules = {}; function __webpack_require__(moduleId) { if (installedModules[moduleId]) return installedModules[moduleId].exports; var module = installedModules[moduleId] = { exports: {}, id: moduleId, loaded: false }; modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); module.loaded = true; return module.exports; } __webpack_require__.m = modules; __webpack_require__.c = installedModules; __webpack_require__.p = ""; return __webpack_require__(0); /* return module.exports = __webpack_require__(1); return module.exports = null; 即返回结果为一个(空)对象。 */ })([function (module, exports, __webpack_require__) { module.exports = __webpack_require__(1); },function (module, exports) { ‘use strict‘; exports.__esModule = true; exports[‘default‘] = thunkMiddleware;//这是最终的注入 function thunkMiddleware(_ref) { var dispatch = _ref.dispatch; var getState = _ref.getState; return function (next) {// return function (store.dispatch) return function (action) { if (typeof action === ‘function‘) { return action(dispatch, getState); } return next(action); }; }; } } ]) } ); //附:instrument.js ‘use strict‘; exports.__esModule = true; exports.INIT_ACTION = exports.ActionCreators = exports.ActionTypes = undefined; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; exports.liftAction = liftAction; exports.liftReducerWith = liftReducerWith; exports.unliftState = unliftState; exports.unliftStore = unliftStore; exports.default = instrument; var _difference = require(‘lodash/difference‘); var _difference2 = _interopRequireDefault(_difference); var _union = require(‘lodash/union‘); var _union2 = _interopRequireDefault(_union); var _isPlainObject = require(‘lodash/isPlainObject‘); var _isPlainObject2 = _interopRequireDefault(_isPlainObject); var _symbolObservable = require(‘symbol-observable‘); var _symbolObservable2 = _interopRequireDefault(_symbolObservable); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var ActionTypes = exports.ActionTypes = { PERFORM_ACTION: ‘PERFORM_ACTION‘, RESET: ‘RESET‘, ROLLBACK: ‘ROLLBACK‘, COMMIT: ‘COMMIT‘, SWEEP: ‘SWEEP‘, TOGGLE_ACTION: ‘TOGGLE_ACTION‘, SET_ACTIONS_ACTIVE: ‘SET_ACTIONS_ACTIVE‘, JUMP_TO_STATE: ‘JUMP_TO_STATE‘, JUMP_TO_ACTION: ‘JUMP_TO_ACTION‘, REORDER_ACTION: ‘REORDER_ACTION‘, IMPORT_STATE: ‘IMPORT_STATE‘, LOCK_CHANGES: ‘LOCK_CHANGES‘, PAUSE_RECORDING: ‘PAUSE_RECORDING‘ }; var isChrome = (typeof window === ‘undefined‘ ? ‘undefined‘ : _typeof(window)) === ‘object‘ && (typeof window.chrome !== ‘undefined‘ || typeof window.process !== ‘undefined‘ && window.process.type === ‘renderer‘); var isChromeOrNode = isChrome || typeof process !== ‘undefined‘ && process.release && process.release.name === ‘node‘; /** * Action creators to change the History state. */ var ActionCreators = exports.ActionCreators = { performAction: function performAction(action, trace, traceLimit, toExcludeFromTrace) { if (!(0, _isPlainObject2.default)(action)) { throw new Error(‘Actions must be plain objects. ‘ + ‘Use custom middleware for async actions.‘); } if (typeof action.type === ‘undefined‘) { throw new Error(‘Actions may not have an undefined "type" property. ‘ + ‘Have you misspelled a constant?‘); } var stack = void 0; if (trace) { var extraFrames = 0; if (typeof trace === ‘function‘) { stack = trace(action); } else { var error = Error(); var prevStackTraceLimit = void 0; if (Error.captureStackTrace && isChromeOrNode) { // avoid error-polyfill if (Error.stackTraceLimit < traceLimit) { prevStackTraceLimit = Error.stackTraceLimit; Error.stackTraceLimit = traceLimit; } Error.captureStackTrace(error, toExcludeFromTrace); } else { extraFrames = 3; } stack = error.stack; if (prevStackTraceLimit) Error.stackTraceLimit = prevStackTraceLimit; if (extraFrames || typeof Error.stackTraceLimit !== ‘number‘ || Error.stackTraceLimit > traceLimit) { var frames = stack.split(‘\n‘); if (frames.length > traceLimit) { stack = frames.slice(0, traceLimit + extraFrames + (frames[0] === ‘Error‘ ? 1 : 0)).join(‘\n‘); } } } } return { type: ActionTypes.PERFORM_ACTION, action: action, timestamp: Date.now(), stack: stack }; }, reset: function reset() { return { type: ActionTypes.RESET, timestamp: Date.now() }; }, rollback: function rollback() { return { type: ActionTypes.ROLLBACK, timestamp: Date.now() }; }, commit: function commit() { return { type: ActionTypes.COMMIT, timestamp: Date.now() }; }, sweep: function sweep() { return { type: ActionTypes.SWEEP }; }, toggleAction: function toggleAction(id) { return { type: ActionTypes.TOGGLE_ACTION, id: id }; }, setActionsActive: function setActionsActive(start, end) { var active = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; return { type: ActionTypes.SET_ACTIONS_ACTIVE, start: start, end: end, active: active }; }, reorderAction: function reorderAction(actionId, beforeActionId) { return { type: ActionTypes.REORDER_ACTION, actionId: actionId, beforeActionId: beforeActionId }; }, jumpToState: function jumpToState(index) { return { type: ActionTypes.JUMP_TO_STATE, index: index }; }, jumpToAction: function jumpToAction(actionId) { return { type: ActionTypes.JUMP_TO_ACTION, actionId: actionId }; }, importState: function importState(nextLiftedState, noRecompute) { return { type: ActionTypes.IMPORT_STATE, nextLiftedState: nextLiftedState, noRecompute: noRecompute }; }, lockChanges: function lockChanges(status) { return { type: ActionTypes.LOCK_CHANGES, status: status }; }, pauseRecording: function pauseRecording(status) { return { type: ActionTypes.PAUSE_RECORDING, status: status }; } }; var INIT_ACTION = exports.INIT_ACTION = { type: ‘@@INIT‘ }; /** * Computes the next entry with exceptions catching. */ function computeWithTryCatch(reducer, action, state) { var nextState = state; var nextError = void 0; try { nextState = reducer(state, action); } catch (err) { nextError = err.toString(); if (isChrome) { // In Chrome, rethrowing provides better source map support setTimeout(function () { throw err; }); } else { console.error(err); } } return { state: nextState, error: nextError }; } /** * Computes the next entry in the log by applying an action. */ function computeNextEntry(reducer, action, state, shouldCatchErrors) { if (!shouldCatchErrors) { return { state: reducer(state, action) }; } return computeWithTryCatch(reducer, action, state); } /** * Runs the reducer on invalidated actions to get a fresh computation log. */ function recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, shouldCatchErrors) { // Optimization: exit early and return the same reference // if we know nothing could have changed. if (!computedStates || minInvalidatedStateIndex === -1 || minInvalidatedStateIndex >= computedStates.length && computedStates.length === stagedActionIds.length) { return computedStates; } var nextComputedStates = computedStates.slice(0, minInvalidatedStateIndex); for (var i = minInvalidatedStateIndex; i < stagedActionIds.length; i++) { var actionId = stagedActionIds[i]; var action = actionsById[actionId].action; var previousEntry = nextComputedStates[i - 1]; var previousState = previousEntry ? previousEntry.state : committedState; var shouldSkip = skippedActionIds.indexOf(actionId) > -1; var entry = void 0; if (shouldSkip) { entry = previousEntry; } else { if (shouldCatchErrors && previousEntry && previousEntry.error) { entry = { state: previousState, error: ‘Interrupted by an error up the chain‘ }; } else { entry = computeNextEntry(reducer, action, previousState, shouldCatchErrors); } } nextComputedStates.push(entry); } return nextComputedStates; } /** * Lifts an app‘s action into an action on the lifted store. */ function liftAction(action, trace, traceLimit, toExcludeFromTrace) { return ActionCreators.performAction(action, trace, traceLimit, toExcludeFromTrace); } /** * Creates a history state reducer from an app‘s reducer. */ function liftReducerWith(reducer, initialCommittedState, monitorReducer, options) { var initialLiftedState = { monitorState: monitorReducer(undefined, {}), nextActionId: 1, actionsById: { 0: liftAction(INIT_ACTION) }, stagedActionIds: [0], skippedActionIds: [], committedState: initialCommittedState, currentStateIndex: 0, computedStates: [], isLocked: options.shouldStartLocked === true, isPaused: options.shouldRecordChanges === false }; /** * Manages how the history actions modify the history state. */ return function (liftedState, liftedAction) { var _ref = liftedState || initialLiftedState, monitorState = _ref.monitorState, actionsById = _ref.actionsById, nextActionId = _ref.nextActionId, stagedActionIds = _ref.stagedActionIds, skippedActionIds = _ref.skippedActionIds, committedState = _ref.committedState, currentStateIndex = _ref.currentStateIndex, computedStates = _ref.computedStates, isLocked = _ref.isLocked, isPaused = _ref.isPaused; if (!liftedState) { // Prevent mutating initialLiftedState actionsById = _extends({}, actionsById); } function commitExcessActions(n) { // Auto-commits n-number of excess actions. var excess = n; var idsToDelete = stagedActionIds.slice(1, excess + 1); for (var i = 0; i < idsToDelete.length; i++) { if (computedStates[i + 1].error) { // Stop if error is found. Commit actions up to error. excess = i; idsToDelete = stagedActionIds.slice(1, excess + 1); break; } else { delete actionsById[idsToDelete[i]]; } } skippedActionIds = skippedActionIds.filter(function (id) { return idsToDelete.indexOf(id) === -1; }); stagedActionIds = [0].concat(stagedActionIds.slice(excess + 1)); committedState = computedStates[excess].state; computedStates = computedStates.slice(excess); currentStateIndex = currentStateIndex > excess ? currentStateIndex - excess : 0; } function computePausedAction(shouldInit) { var _extends2; var computedState = void 0; if (shouldInit) { computedState = computedStates[currentStateIndex]; monitorState = monitorReducer(monitorState, liftedAction); } else { computedState = computeNextEntry(reducer, liftedAction.action, computedStates[currentStateIndex].state, false); } if (!options.pauseActionType || nextActionId === 1) { return { monitorState: monitorState, actionsById: { 0: liftAction(INIT_ACTION) }, nextActionId: 1, stagedActionIds: [0], skippedActionIds: [], committedState: computedState.state, currentStateIndex: 0, computedStates: [computedState], isLocked: isLocked, isPaused: true }; } if (shouldInit) { if (currentStateIndex === stagedActionIds.length - 1) { currentStateIndex++; } stagedActionIds = [].concat(stagedActionIds, [nextActionId]); nextActionId++; } return { monitorState: monitorState, actionsById: _extends({}, actionsById, (_extends2 = {}, _extends2[nextActionId - 1] = liftAction({ type: options.pauseActionType }), _extends2)), nextActionId: nextActionId, stagedActionIds: stagedActionIds, skippedActionIds: skippedActionIds, committedState: committedState, currentStateIndex: currentStateIndex, computedStates: [].concat(computedStates.slice(0, stagedActionIds.length - 1), [computedState]), isLocked: isLocked, isPaused: true }; } // By default, agressively recompute every state whatever happens. // This has O(n) performance, so we‘ll override this to a sensible // value whenever we feel like we don‘t have to recompute the states. var minInvalidatedStateIndex = 0; // maxAge number can be changed dynamically var maxAge = options.maxAge; if (typeof maxAge === ‘function‘) maxAge = maxAge(liftedAction, liftedState); if (/^@@redux\/(INIT|REPLACE)/.test(liftedAction.type)) { if (options.shouldHotReload === false) { actionsById = { 0: liftAction(INIT_ACTION) }; nextActionId = 1; stagedActionIds = [0]; skippedActionIds = []; committedState = computedStates.length === 0 ? initialCommittedState : computedStates[currentStateIndex].state; currentStateIndex = 0; computedStates = []; } // Recompute states on hot reload and init. minInvalidatedStateIndex = 0; if (maxAge && stagedActionIds.length > maxAge) { // States must be recomputed before committing excess. computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, options.shouldCatchErrors); commitExcessActions(stagedActionIds.length - maxAge); // Avoid double computation. minInvalidatedStateIndex = Infinity; } } else { switch (liftedAction.type) { case ActionTypes.PERFORM_ACTION: { if (isLocked) return liftedState || initialLiftedState; if (isPaused) return computePausedAction(); // Auto-commit as new actions come in. if (maxAge && stagedActionIds.length >= maxAge) { commitExcessActions(stagedActionIds.length - maxAge + 1); } if (currentStateIndex === stagedActionIds.length - 1) { currentStateIndex++; } var actionId = nextActionId++; // Mutation! This is the hottest path, and we optimize on purpose. // It is safe because we set a new key in a cache dictionary. actionsById[actionId] = liftedAction; stagedActionIds = [].concat(stagedActionIds, [actionId]); // Optimization: we know that only the new action needs computing. minInvalidatedStateIndex = stagedActionIds.length - 1; break; } case ActionTypes.RESET: { // Get back to the state the store was created with. actionsById = { 0: liftAction(INIT_ACTION) }; nextActionId = 1; stagedActionIds = [0]; skippedActionIds = []; committedState = initialCommittedState; currentStateIndex = 0; computedStates = []; break; } case ActionTypes.COMMIT: { // Consider the last committed state the new starting point. // Squash any staged actions into a single committed state. actionsById = { 0: liftAction(INIT_ACTION) }; nextActionId = 1; stagedActionIds = [0]; skippedActionIds = []; committedState = computedStates[currentStateIndex].state; currentStateIndex = 0; computedStates = []; break; } case ActionTypes.ROLLBACK: { // Forget about any staged actions. // Start again from the last committed state. actionsById = { 0: liftAction(INIT_ACTION) }; nextActionId = 1; stagedActionIds = [0]; skippedActionIds = []; currentStateIndex = 0; computedStates = []; break; } case ActionTypes.TOGGLE_ACTION: { // Toggle whether an action with given ID is skipped. // Being skipped means it is a no-op during the computation. var _actionId = liftedAction.id; var index = skippedActionIds.indexOf(_actionId); if (index === -1) { skippedActionIds = [_actionId].concat(skippedActionIds); } else { skippedActionIds = skippedActionIds.filter(function (id) { return id !== _actionId; }); } // Optimization: we know history before this action hasn‘t changed minInvalidatedStateIndex = stagedActionIds.indexOf(_actionId); break; } case ActionTypes.SET_ACTIONS_ACTIVE: { // Toggle whether an action with given ID is skipped. // Being skipped means it is a no-op during the computation. var start = liftedAction.start, end = liftedAction.end, active = liftedAction.active; var actionIds = []; for (var i = start; i < end; i++) { actionIds.push(i); }if (active) { skippedActionIds = (0, _difference2.default)(skippedActionIds, actionIds); } else { skippedActionIds = (0, _union2.default)(skippedActionIds, actionIds); } // Optimization: we know history before this action hasn‘t changed minInvalidatedStateIndex = stagedActionIds.indexOf(start); break; } case ActionTypes.JUMP_TO_STATE: { // Without recomputing anything, move the pointer that tell us // which state is considered the current one. Useful for sliders. currentStateIndex = liftedAction.index; // Optimization: we know the history has not changed. minInvalidatedStateIndex = Infinity; break; } case ActionTypes.JUMP_TO_ACTION: { // Jumps to a corresponding state to a specific action. // Useful when filtering actions. var _index = stagedActionIds.indexOf(liftedAction.actionId); if (_index !== -1) currentStateIndex = _index; minInvalidatedStateIndex = Infinity; break; } case ActionTypes.SWEEP: { // Forget any actions that are currently being skipped. stagedActionIds = (0, _difference2.default)(stagedActionIds, skippedActionIds); skippedActionIds = []; currentStateIndex = Math.min(currentStateIndex, stagedActionIds.length - 1); break; } case ActionTypes.REORDER_ACTION: { // Recompute actions in a new order. var _actionId2 = liftedAction.actionId; var idx = stagedActionIds.indexOf(_actionId2); // do nothing in case the action is already removed or trying to move the first action if (idx < 1) break; var beforeActionId = liftedAction.beforeActionId; var newIdx = stagedActionIds.indexOf(beforeActionId); if (newIdx < 1) { // move to the beginning or to the end var count = stagedActionIds.length; newIdx = beforeActionId > stagedActionIds[count - 1] ? count : 1; } var diff = idx - newIdx; if (diff > 0) { // move left stagedActionIds = [].concat(stagedActionIds.slice(0, newIdx), [_actionId2], stagedActionIds.slice(newIdx, idx), stagedActionIds.slice(idx + 1)); minInvalidatedStateIndex = newIdx; } else if (diff < 0) { // move right stagedActionIds = [].concat(stagedActionIds.slice(0, idx), stagedActionIds.slice(idx + 1, newIdx), [_actionId2], stagedActionIds.slice(newIdx)); minInvalidatedStateIndex = idx; } break; } case ActionTypes.IMPORT_STATE: { if (Array.isArray(liftedAction.nextLiftedState)) { // recompute array of actions actionsById = { 0: liftAction(INIT_ACTION) }; nextActionId = 1; stagedActionIds = [0]; skippedActionIds = []; currentStateIndex = liftedAction.nextLiftedState.length; computedStates = []; committedState = liftedAction.preloadedState; minInvalidatedStateIndex = 0; // iterate through actions liftedAction.nextLiftedState.forEach(function (action) { actionsById[nextActionId] = liftAction(action, options.trace || options.shouldIncludeCallstack); stagedActionIds.push(nextActionId); nextActionId++; }); } else { var _liftedAction$nextLif = liftedAction.nextLiftedState; // Completely replace everything. monitorState = _liftedAction$nextLif.monitorState; actionsById = _liftedAction$nextLif.actionsById; nextActionId = _liftedAction$nextLif.nextActionId; stagedActionIds = _liftedAction$nextLif.stagedActionIds; skippedActionIds = _liftedAction$nextLif.skippedActionIds; committedState = _liftedAction$nextLif.committedState; currentStateIndex = _liftedAction$nextLif.currentStateIndex; computedStates = _liftedAction$nextLif.computedStates; if (liftedAction.noRecompute) { minInvalidatedStateIndex = Infinity; } } break; } case ActionTypes.LOCK_CHANGES: { isLocked = liftedAction.status; minInvalidatedStateIndex = Infinity; break; } case ActionTypes.PAUSE_RECORDING: { isPaused = liftedAction.status; if (isPaused) { return computePausedAction(true); } // Commit when unpausing actionsById = { 0: liftAction(INIT_ACTION) }; nextActionId = 1; stagedActionIds = [0]; skippedActionIds = []; committedState = computedStates[currentStateIndex].state; currentStateIndex = 0; computedStates = []; break; } default: { // If the action is not recognized, it‘s a monitor action. // Optimization: a monitor action can‘t change history. minInvalidatedStateIndex = Infinity; break; } } } computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, options.shouldCatchErrors); monitorState = monitorReducer(monitorState, liftedAction); return { monitorState: monitorState, actionsById: actionsById, nextActionId: nextActionId, stagedActionIds: stagedActionIds, skippedActionIds: skippedActionIds, committedState: committedState, currentStateIndex: currentStateIndex, computedStates: computedStates, isLocked: isLocked, isPaused: isPaused }; }; } /** * Provides an app‘s view into the state of the lifted store. */ function unliftState(liftedState) { var computedStates = liftedState.computedStates, currentStateIndex = liftedState.currentStateIndex; var state = computedStates[currentStateIndex].state; return state; } /** * Provides an app‘s view into the lifted store. */ function unliftStore(liftedStore, liftReducer, options) { var _extends3; var lastDefinedState = void 0; var trace = options.trace || options.shouldIncludeCallstack; var traceLimit = options.traceLimit || 10; function getState() { var state = unliftState(liftedStore.getState()); if (state !== undefined) { lastDefinedState = state; } return lastDefinedState; } function dispatch(action) { liftedStore.dispatch(liftAction(action, trace, traceLimit, dispatch)); return action; } return _extends({}, liftedStore, (_extends3 = { liftedStore: liftedStore, dispatch: dispatch, getState: getState, replaceReducer: function replaceReducer(nextReducer) { liftedStore.replaceReducer(liftReducer(nextReducer)); } }, _extends3[_symbolObservable2.default] = function () { return _extends({}, liftedStore[_symbolObservable2.default](), { subscribe: function subscribe(observer) { if ((typeof observer === ‘undefined‘ ? ‘undefined‘ : _typeof(observer)) !== ‘object‘) { throw new TypeError(‘Expected the observer to be an object.‘); } function observeState() { if (observer.next) { observer.next(getState()); } } observeState(); var unsubscribe = liftedStore.subscribe(observeState); return { unsubscribe: unsubscribe }; } }); }, _extends3)); } /** * Redux instrumentation store enhancer. */ function instrument() { var monitorReducer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () { return null; }; var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (typeof options.maxAge === ‘number‘ && options.maxAge < 2) { throw new Error(‘DevTools.instrument({ maxAge }) option, if specified, ‘ + ‘may not be less than 2.‘); } return function (createStore) { return function (reducer, initialState, enhancer) { function liftReducer(r) { if (typeof r !== ‘function‘) { if (r && typeof r.default === ‘function‘) { throw new Error(‘Expected the reducer to be a function. ‘ + ‘Instead got an object with a "default" field. ‘ + ‘Did you pass a module instead of the default export? ‘ + ‘Try passing require(...).default instead.‘); } throw new Error(‘Expected the reducer to be a function.‘); } return liftReducerWith(r, initialState, monitorReducer, options); } var liftedStore = createStore(liftReducer(reducer), enhancer); if (liftedStore.liftedStore) { throw new Error(‘DevTools instrumentation should not be applied more than once. ‘ + ‘Check your store configuration.‘); } return unliftStore(liftedStore, liftReducer, options); }; }; }
原文:https://www.cnblogs.com/gushixianqiancheng/p/13392540.html