Preliminary implementation of Redux core code

Time:2022-6-20
/**
 *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>

Recommended Today

Example of interaction between Lua and C language

Lua is a lightweight, flexible and highly extensible scripting language. It can be easily embedded in other languages (c/c++), mainly thanks to its powerful C API, which makes it easy to interoperate with c/c++. Lua calls C When Lua calls the C function, it actually registers the C function in Lua and passes the C […]