Redux
Read the redux source code
RandomString generates a randomString
var randomString = function randomString() {
return Math.random().toString(36).substring(7).split(' ').join('. ');
};
Copy the code
Number. The prototype. The toString () : the toString () method returns a string representation of the specified Number object.
numObj.toString([radix])
- If the RADIX parameter is not specified, the default value is 10;
- If the Radix parameter is not between 2-36, a RangeError will be raised
- If the base of the conversion is greater than 10, letters are used for numbers greater than 9, for example, if the base is 16, letters A through F are used for 10 to 15.
A recent question that comes to mind [1.2.3.2].map(parseInt) // [1, NaN, NaN, 2]
Copy the code
IsPlainObject Determines whether a function is stored
function isPlainObject(obj) {
if (typeofobj ! = ='object' || obj === null) return false;
var proto = obj;
while (Object.getPrototypeOf(proto) ! = =null) {
proto = Object.getPrototypeOf(proto);
}
return Object.getPrototypeOf(obj) === proto;
}
Copy the code
Why does it have to be so complicated?
- Obj may be generated from other runtime environments (such as syndomain IFrame in browser, VM in NodeJS)
- Of course, it is possible to make the code behave differently
let obj = Object.create(Object.create(null));
// Write it literally like this:
let obj = {
__proto__: {
__proto__: null}}; isPlainObject(obj)// true
isPlainObject(null) // false
Copy the code
CreateStore creates a Redux state tree and the return value is a Store
- Reducer is a function that accepts the current state and the actions to be processed and returns a new state
- PreloadedState Initialization state
- Enhancer Store enhancer
function createStore(reducer, preloadedState, enhancer) {
var _ref2;
If preloadedState is a function and enhancer is a function, or if enhancer and the fourth argument are functions, then an error is thrown
if (typeof preloadedState === 'function' && typeof enhancer === 'function' || typeof enhancer === 'function' && typeof arguments[3= = ='function') {
throw new Error('It looks like you are passing several store enhancers to ' + 'createStore(). This is not supported. Instead, compose them ' + 'together to a single function.');
}
// If preloadedState is a function and the enhancer is not passed, the function is assigned as an enhancer and the enhancer initialization state is assigned as undefined
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState;
preloadedState = undefined;
}
If the enhancer is not empty but not a function, an error is thrown
if (typeofenhancer ! = ='undefined') {
if (typeofenhancer ! = ='function') {
throw new Error('Expected the enhancer to be a function.');
}
Serv (serv); serv (serv); serv (serV); serv (serv); serv (serv); serv (serv)
return enhancer(createStore)(reducer, preloadedState);
}
If reducer is not a function, it must be a function
if (typeofreducer ! = ='function') {
throw new Error('Expected the reducer to be a function.');
}
/ / the current reducer
var currentReducer = reducer;
/ / the current state
var currentState = preloadedState;
// The current listener
var currentListeners = [];
var nextListeners = currentListeners;
var isDispatching = false;
Implement a shallow copy of currentListeners
function ensureCanMutateNextListeners() {
if(nextListeners === currentListeners) { nextListeners = currentListeners.slice(); }}// Get the state of the current application, not at dispatch, otherwise an error will be reported
function getState() {
if (isDispatching) {
throw new Error('You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.');
}
return currentState;
}
// Add a listener that will be called every time the subscription is dispatched. The return value is a function that can be used to close the subscription
function subscribe(listener) {
// The subscription function must be a function that can be called
if (typeoflistener ! = ='function') {
throw new Error('Expected the listener to be a function.');
}
// An error is thrown if the call is still made from dispatch
if (isDispatching) {
throw new Error('You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribelistener for more details.');
}
var isSubscribed = true;
Shallow copy currentListener to nextListeners
ensureCanMutateNextListeners();
// Then add the new listener to the end of the shallow copy
nextListeners.push(listener);
// This is the return value unsubscribe
return function unsubscribe() {
// Return if the subscription has already been unsubscribed
if(! isSubscribed) {return;
}
// As above, never do anything at dispatch
if (isDispatching) {
throw new Error('You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribelistener for more details.');
}
// The tag has been unsubscribed. This is a closure function that gets the parameters accessible for each call. The judgment of the isSubscribed
isSubscribed = false;
// A snapshot of the current is generated at dispatch
ensureCanMutateNextListeners();
var index = nextListeners.indexOf(listener);
// Find and delete
nextListeners.splice(index, 1);
// TODO:Then empty the currentListeners
currentListeners = null;
};
}
// The only way to change state for an action is if an object with no side effects returns the action passed in
function dispatch(action) {
// Check whether the action is present or not
if(! isPlainObject(action)) {throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
}
// Specify that the action must have the type attribute and cannot be undefined
if (typeof action.type === 'undefined') {
throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant? ');
}
// The reducer cannot dispatch
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
isDispatching = true;
// currentReducer (reducer) accepts two parameters. CurrentState currentState may also be undefined
currentState = currentReducer(currentState, action);
} finally {
// When the reducer is complete, isDispatching becomes false
isDispatching = false;
}
// A snapshot of the latest listener is generated after each unsubscribe shallow copy
var listeners = currentListeners = nextListeners;
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
listener();
}
return action;
}
// Replace reducer and change state returns null
function replaceReducer(nextReducer) {
The reducer is not a function
if (typeofnextReducer ! = ='function') {
throw new Error('Expected the nextReducer to be a function.');
}
currentReducer = nextReducer;
// Initiate a replace action
dispatch({
type: ActionTypes.REPLACE
});
}
function observable() {
var _ref;
// The subscribe function defined above takes a listener as an argument and returns a function that removes the current listener
var outerSubscribe = subscribe;
return _ref = {
// A subscribe function takes an object with the next attribute and returns an object with the unsubscribede
subscribe: function subscribe(observer) {
// An error is reported if the observer is not an object
if (typeofobserver ! = ='object' || observer === null) {
throw new TypeError('Expected the observer to be an object.');
}
// https://github.com/tc39/proposal-observable
function observeState() {
if (observer.next) {
// The current state is passed inobserver.next(getState()); }}// Initialize once
observeState();
// Register observeState listener
var unsubscribe = outerSubscribe(observeState);
return {
unsubscribe: unsubscribe
};
}
}, _ref[$$observable] = function () {
// TODO
return this;
}, _ref; // this is a comma expression that returns _ref
}
Create an initial state from reducer when a store is created
dispatch({
type: ActionTypes.INIT
});
The _ref2 store contains a dispatch/subscribe/getState/replaceReducer and an Observable
return _ref2 = {
dispatch: dispatch,
subscribe: subscribe,
getState: getState,
replaceReducer: replaceReducer
}, _ref2[$$observable] = observable, _ref2;
}
Copy the code
Warning Displays warning information on the console
function warning(message) {
// It is a good practice to determine whether console exists in a different context before invoking it
if (typeof console! = ='undefined' && typeof console.error === 'function') {
console.error(message);
}
try {
throw new Error(message);
} catch (e) {}
}
Copy the code
When you return to undefined getUndefinedStateErrorMessage when reducer through the action of the error message
function getUndefinedStateErrorMessage(key, action) {
var actionType = action && action.type;
var actionDescription = actionType && "action \"" + String(actionType) + "\" " || 'an action';
return "Given " + actionDescription + ", reducer \"" + key + "\" returned undefined. " + "To ignore an action, you must explicitly return the previous state. " + "If you want this reducer to hold no value, you can return null instead of undefined.";
}
Copy the code
Warned getUnexpectedStateShapeWarningMessage by comparing the state and the attribute of reducer
function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
var reducerKeys = Object.keys(reducers);
var argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer';
// Do not have a reducer, otherwise an error is reported
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 the state is not stored, the error is reported
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(function (key) {
return! reducers.hasOwnProperty(key) && ! unexpectedKeyCache[key]; }); unexpectedKeys.forEach(function (key) {
unexpectedKeyCache[key] = true;
});
/ / REPLACE
if (action && action.type === ActionTypes.REPLACE) return;
// Otherwise, there must be a reducer for each state's attributes
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."); }}Copy the code
AssertReducerShape determines whether reducers are legitimate
function assertReducerShape(reducers) {
Object.keys(reducers).forEach(function (key) {
var reducer = reducers[key];
var initialState = reducer(undefined, {
type: ActionTypes.INIT
});
// An error is reported if state is returned during initialization or when state is given to undefined
if (typeof initialState === 'undefined') {
throw new Error("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. If you don't want to set a value for this reducer, " + "you can use null instead of undefined.");
}
// It must be able to catch an unknown action with default returning the current state or an error will be reported
if (typeof reducer(undefined, {
type: ActionTypes.PROBE_UNKNOWN_ACTION()
}) === 'undefined') {
throw new Error("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, but can be null."); }}); }Copy the code
CombineReducers integrate reducers into a single Reducer/return value is a function that receives the initial state and receives an action that returns the changed state
function combineReducers(reducers) {
var reducerKeys = Object.keys(reducers);
var finalReducers = {};
// Go through the reducer
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
if(process.env.NODE_ENV ! = ='production') {
// A warning message will be printed on the console if it is not in production
if (typeof reducers[key] === 'undefined') {
warning("No reducer provided for key \"" + key + "\" "); }}// By judging whether the function is the final reducers formed, it can also be removed to prevent the passed reducers from having duplicate keys
if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key]; }}var finalReducerKeys = Object.keys(finalReducers);
var unexpectedKeyCache;
if(process.env.NODE_ENV ! = ='production') {
unexpectedKeyCache = {};
}
var shapeAssertionError;
try {
// Determine whether reducers are legal
assertReducerShape(finalReducers);
} catch (e) {
shapeAssertionError = e;
}
// The return value is a function that accepts a state and an action
return function combination(state, action) {
// If state is not passed, an empty object is passed
if (state === void 0) {
state = {};
}
If the reducer does not meet the specification, the reducer should reduce the error
if (shapeAssertionError) {
throw shapeAssertionError;
}
if(process.env.NODE_ENV ! = ='production') {
// If the reducer is not a production environment, check whether the incoming state has keys. No reducer is available
var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);
if(warningMessage) { warning(warningMessage); }}var hasChanged = false;
var nextState = {};
// Walk through the final reducers
for (var _i = 0; _i < finalReducerKeys.length; _i++) {
var _key = finalReducerKeys[_i];
var reducer = finalReducers[_key];
var previousStateForKey = state[_key];
// previousStateForKey may be undefined so reducer must have a default state
var nextStateForKey = reducer(previousStateForKey, action);
if (typeof nextStateForKey === 'undefined') {
// An error is reported if the default state is not given
var errorMessage = getUndefinedStateErrorMessage(_key, action);
throw new Error(errorMessage);
}
nextState[_key] = nextStateForKey;
// Check whether state changes by comparinghasChanged = hasChanged || nextStateForKey ! == previousStateForKey; } hasChanged = hasChanged || finalReducerKeys.length ! = =Object.keys(state).length;
// If the reducer changes the state values or the reducer and state values do not match, the latest state is returned => nextState
return hasChanged ? nextState : state;
};
}
Copy the code
BindActionCreator takes an actionCreator function and the second argument is Dispatch
function bindActionCreator(actionCreator, dispatch) {
// Returns a function dispatch an action
return function () {
return dispatch(actionCreator.apply(this.arguments));
};
}
Copy the code
The first parameter of bindActionCreators can be either function or an object whose value is actionCreator and the second parameter is Dispatch and the return value is the function that uses Dispatch or the object whose value is creators
function bindActionCreators(actionCreators, dispatch) {
// If the first argument is a function, return the function wrapped by Dispatch
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch);
}
// An error is thrown if the object is not excluded to null
if (typeofactionCreators ! = ='object' || actionCreators === null) {
throw new Error("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\"?");
}
var boundActionCreators = {};
for (var key in actionCreators) {
// hasOwnProperty
var actionCreator = actionCreators[key];
if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); }}// The return value is one function for each function wrapped in dispatch
return boundActionCreators;
}
Copy the code
_defineProperty Object. function of defineProperty
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true.configurable: true.writable: true
});
} else {
obj[key] = value;
}
return obj;
}
Copy the code
OwnKeys takes an argument and an enumerable argument returns all keys
function ownKeys(object, enumerableOnly) {
// All keys on the object but not the symbol key
var keys = Object.keys(object);
// Add the Symbol key
if (Object.getOwnPropertySymbols) {
keys.push.apply(keys, Object.getOwnPropertySymbols(object));
}
if (enumerableOnly) keys = keys.filter(function (sym) {
Accessor properties
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
return keys;
}
Copy the code
_objectSpread2
function _objectSpread2(target) {
// If none of the arguments are returned directly
for (var i = 1; i < arguments.length; i++) {
/ / can't be null because can't use the Object to null or undefined. GetOwnPropertyDescriptors
var source = arguments[i] ! =null ? arguments[i] : {};
// Aggregate all parameters into an Object
if (i % 2) {
ownKeys(source, true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(source).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); }}return target;
}
Copy the code
Compose Currified (… args) => f(g(h(… Args))) are executed from right to left
function compose() {
// Iterate over the parameters to generate an array of all parameters
for (var _len = arguments.length, funcs = new Array(_len), _key = 0; _key < _len; _key++) {
funcs[_key] = arguments[_key];
}
// Generate a function if there are no arguments
if (funcs.length === 0) {
return function (arg) {
return arg;
};
}
// Return the current function if there is only one function
if (funcs.length === 1) {
return funcs[0];
}
// A reduce function is wrapped from left to right with the last one at the bottom
return funcs.reduce(function (a, b) {
return function () {
return a(b.apply(void 0.arguments));
};
});
}
Copy the code
ApplyMiddleware takes some middleware and generates a complete enhancer
function applyMiddleware() {
// Same as above to an array of middleware
for (var _len = arguments.length, middlewares = new Array(_len), _key = 0; _key < _len; _key++) {
middlewares[_key] = arguments[_key];
}
// Returns a function that takes createStore
return function (createStore) {
return function () {
// Generate a store
var store = createStore.apply(void 0.arguments);
// Cannot be dispatched while building middleware because the rest of the middleware will not receive the dispatch if it is called
var _dispatch = function dispatch() {
throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.');
};
// Pass in an error-reporting dispatch to prevent dispatch from being called while building middleware
var middlewareAPI = {
getState: store.getState,
dispatch: function dispatch() {
return _dispatch.apply(void 0.arguments); }};// Generate an array of middleware
var chain = middlewares.map(function (middleware) {
return middleware(middlewareAPI);
});
{getState, dispatch} returns a function that receives a dispatch
= compose.apply(void 0, chain)(store.dispatch);
// Generate an enhanced store object
return _objectSpread2({}, store, {
dispatch: _dispatch
});
};
};
}
Copy the code
IsCrushed false function
function isCrushed() {}
// An alarm is generated if the function name is compressed in a non-production environment
if(process.env.NODE_ENV ! = ='production' && typeof isCrushed.name === 'string'&& isCrushed.name ! = ='isCrushed') {
warning('You are currently using minified code outside of NODE_ENV === "production". ' + 'This means that you are running a slower development build of Redux. ' + 'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' + 'or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' + 'to ensure you have the correct code for your production build.');
}
Copy the code
That’s all the Redux code
Here is an example to reinforce it again
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import reducers from './reducers';
// Suppose there are two middleware components
import thunk from 'redux-thunk';
// https://github.com/fcomb/redux-logger.git
const logMiddleware = ({ getState }) = > dispatch= > {
return action= > {
const current prevState = getState()
const returnValue = dispatch(action)
const nextValue = getState()
if (typeof console! = ='undefined') {
const message = `action ${action.type} @ ${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}`;
try {
console.group(message);
} catch(e) {
console.log('NOT GROUP');
}
console.log(`%c prev state`.`color: #9E9E9E; font-weight: bold`, prevState);
console.log(`%c action`.`color: #03A9F4; font-weight: bold`, action);
console.log(`%c next state`.`color: #4CAF50; font-weight: bold`, nextState);
try {
console.groupEnd('- the log end -);
} catch(e) {
console.log('- the log end -); }}// The return value is action if it does not change
returnreturnValue; }}// Generate middleware collection
const createStoreWithMiddleware = applyMiddleware(logger, thunk)(createStore);
/ / generated reducer
const reducer = combineReducers(reducers);
// Then generate the global store
const store = createStoreWithMiddleware(reducer);
Copy the code