/**
*Createstore (reducer, preloadedstate, and enhancer enhance the store function)
* {
* getState,dispatch,subscribe
* }
*/
function createStore(reducer, preloadedState, enhancer) {
//9. Constraint reducer parameter type
if (typeof reducer !== "function")
throw new Error("reducer must be a function");
//12 judge whether enhancer is passed and whether it is a function
if (typeof enhancer !== "undefined") {
if (typeof enhancer !== "function") {
throw new Error("enhancer must be a function");
}
//Call in Redux, pass in createstore and return a function, return reducer and preloadedstate
return enhancer(createStore)(reducer, preloadedState);
}
//1. Status stored in sotre object
var currentState = preloadedState;
//6. Store subscriber function
var currentListeners = [];
//2. Get status
function getState() {
return currentState;
}
//3 method for triggering action
function dispatch(action) {
//10. Judge whether an action is an object
If (! Isplainobject (action)) throw new error ("action must be an object");
//11. Judge whether the type attribute in the action exists
if (typeof action.type === "undefined")
Throw new error ("the action object must have a type attribute");
currentState = reducer(currentState, action); // Return the new status according to the current status and action processing
//7 circular array call subscriber
for (let i = 0; i < currentListeners.length; i++) {
//Get subscribers
var listener = currentListeners[i];
//Call subscriber
listener();
}
}
//5. Subscription status
function subscribe(listener) {
currentListeners.push(listener);
}
//8 return
return {
getState,
dispatch,
subscribe,
};
}
// 4
// store.subscribe(() => {});
//Judge whether the parameter is an object type
//Judge whether the current prototype object of the object is the same as the top-level prototype object
function isPlainObject(obj) {
//Exclude basic data types and nulls
if (typeof obj !== "object" || obj === null) return false;
//How to distinguish between array and object prototype object comparison
var proto = obj;
//Get the topmost prototype object
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto);
}
return Object. getPrototypeOf(obj) === proto; // Returning true is the object
}
function applyMiddleware(...middlewares) {
return function (createStore) {
return function (reducer, preloadedState) {
//Create a store, get the store, and transfer parameters to the middleware
var store = createStore(reducer, preloadedState);
//Castrated store
var middlewareAPI = {
getState: store.getState,
dispatch: store.dispatch,
};
//Call the first layer function of the middleware, transfer the castrated store object, and return the inner two layers of functions in the middleware function
var chain = middlewares.map((middleware) => middleware(middlewareAPI));
//The second layer of the middleware transfers parameters, and the third layer is dispath
var dispatch = compose(...chain)(store.dispatch);
//Return an enhanced store
return {
...store,
dispatch,
};
};
};
}
function compose() {
var funcs = [...arguments];
console.log(funcs);
//Because of the function nesting problem, although the execution order is logger and thunk, in order to ensure the order, the array should be flashed. Thunk the second layer to return dispath,
return function (dispatch) {
for (var i = funcs.length - 1; i >= 0; i--) {
//The return value of the first round of execution is the function in thunk, which is required by the logger
dispatch = funcs[i](dispatch);
}
return dispatch;
};
}
//The bindactioncreators function converts the action creator function into a function that can trigger an action
function bindActionCreators(actionCreators, dispatch) {
//Create an object to return. The return value is an object
// function increment(){
// dispatch({type:'increment'})
// }
var boundActionCreators = {};
for (var key in actionCreators) {
//Iife solves the problem that key variables cannot be saved
(function (key) {
boundActionCreators[key] = function () {
dispatch(actionCreators[key]());
};
})(key);
//Actioncreators[key] () get the increment function, execute it, and return the action object
//Dispatch (actioncreators[key] ()) dispatch action object
}
return boundActionCreators;
}
//Combinereducers combine small reducers into large reducers, and return a reducer function
function combineReducers(reducers) {
//Check the reducer type. It must be a function
var reducerKeys = Object.keys(reducers);
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
if (typeof reducers[key] !== "function")
Throw new error ("reducer must be a function");
}
//Call small reducers one by one to store the returned state of each small reducer in a new large object
return function (state, action) {
var nextState = {}; // Store the latest status
//Cycle the reducer to get the latest status
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
var reducer = reducers[key];
var previousStateForKey = state[key];
nextState[key] = reducer(previousStateForKey, action);
}
console.log(nextState)
return nextState;
};
}
Test code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id='increment'>+1</button>
<span id='box'>0</span>
<button id='decrement'>-1</button>
<script src="myRedux.js"></script>
<script src="middlewares/logger.js"></script>
<script src="middlewares/thunk.js"></script>
<script>
function enhancer(createStore) {
return function (reducer, preloadedState) {
var store = createStore(reducer, preloadedState)
var dispatch = store.dispatch
function _dispatch(action) {
if (typeof action === 'function') {
return action(dispatch)
}
dispatch(action)
}
return {
...store,
dispatch: _dispatch
}
}
}
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1
default:
return state
}
}
var rootReducer = combineReducers({
counter: counterReducer
})
//1 create a store
// const store = createStore(reducer, 0, enhancer)
const store = createStore(rootReducer, {
counter: 100
}, applyMiddleware(logger, thunk))
console.log(store)
store.subscribe(function () {
//Get the latest status
console.log(store.getState())
document.getElementById('box').innerHTML = store.getState().counter
})
var actions = bindActionCreators({
increment,
decrement
}, store.dispatch)
function increment() {
return {
type: 'increment'
}
}
function decrement() {
return {
type: "decrement"
}
}
//Get add button
document.getElementById('increment').onclick = function () {
//Trigger action
// store.dispatch(function (dispatch) {
// setTimeout(function () {
// dispatch({
// type: 'increment'
// })
// }, 1000);
// })
//Code execution sequence logger->thunk->reducer
// store.dispatch({
// type: 'increment'
// })
actions.increment()
}
document.getElementById('decrement').onclick = function () {
// store.dispatch({
// type: 'decrement'
// })
actions.decrement()
}
</script>
</body>
</html>