The principle of overview

  • To define aStoreclass
  • store.stateUsed to store status data
  • store.dispatch(action)To change thestatestate
  • reducer(state,action)Function to define and associateactionwithstateMapping of change operations
  • store.subscribe(callback)To collectstateThe updated callback function

implementation

Store Class

class Store {
  constructor(initialState, reducers) {
    this.state = initialState || {};
    this.reducer = this.combineReducers(reducers);
    this.subs = [];
  }

  getState = () = > this.state;
  setState = state= > this.state = state;
  subscribe = callback= > this.subs.push(callback.bind({
    state: this.getState()
  }));

  dispatch = action= > {
    this.setState(this.reducer(this.getState(), action));
    this.subs.forEach(sub= > sub());
  }
  combineReducers = (reducers) = > {
    return (state = this.getState(), action) = > {
      // Iterating through the reducers function only changes the contents of the state object, leaving its references unchanged
      Object.keys(reducers).forEach(fnKeyFromReducer= > {
        let newState = reducers[fnKeyFromReducer](state, action);
        // merge old and new states, and finally setState once in dispatch
        // Merge the changes to the original initialState, note the reference address of the same object
        Object.assign(state, newState);
      });
      // Note that the state returned here is still a reference to initialState
      returnstate; }}}Copy the code

createStore

function createStore(initialState = {}, reducers) {
  let store = new Store(initialState, reducers);
  return store;
}
Copy the code

demo

<body>
  <div id="app"></div>
  <button onclick="add()">add</button>
  <button onclick="reduce()">reduce</button>
  <script>
    let obj = {
      count: 0
    };
    
    let reducers = {
      add: function (state, action) {
        if (action.type === 'add') {
          return Object.assign({}, state, {
            count: state.count + 1})}return state;
      },
      reduce: function (state, action) {
        if (action.type === 'reduce') {
          return Object.assign({}, state, {
            count: state.count - 1})}returnstate; }};let store = createStore(obj, reducers);

    store.subscribe(function () {
      console.log(obj === this.state); // true, state is just a change, its original initialState reference remains unchanged
      document.querySelector('#app').textContent = this.state.count;
    });

    function add() {
      store.dispatch({
        type: 'add'})},function reduce() {
      store.dispatch({
        type: 'reduce'})},</script>
</body>
Copy the code

Implement applyMiddleware

Store

class Store {
  constructor(initialState, reducers) {
    this.state = initialState;
    this.reducer = this.combineReducers(reducers);
    this.subs = [];
    this.initialDispatch(); // Initialize the default dispatch
  }

  getState = () = > this.state;
  setState = state= > this.state = state;
  // Bind here only to follow the Demeter principle, i.e., the least known principle, optional
  subscribe = callback= > this.subs.push(callback.bind({
    state: this.getState()
  }));

  initialDispatch = (dispatch) = > {
    if (dispatch) { // The default dispatch is overridden if an external dispatch is passed with an integrated middleware function
      return this.dispatch = dispatch;
    }
    this.dispatch = action= > {
      // Execute the reducers function to change only the contents of the state object, leaving its references unchanged
      this.setState(this.reducer(this.getState(), action));
      this.subs.forEach(sub= > sub());
    }
  }

  combineReducers = (reducers) = > {
    return (state = this.getState(), action) = > {
      Object.keys(reducers).forEach(fnKeyFromReducer= > {
        let newState = reducers[fnKeyFromReducer](state, action);
        // merge old and new states, and finally setState once in dispatch
        // Merge the changes to the original initialState, note the reference address of the same object
        Object.assign(state, newState);
      });
      // Note that the state returned here is still a reference to initialState
      returnstate; }}}Copy the code

createStore && applyMiddleware

// The third argument is the enhanced dispatch
function createStore(initialState = {}, reducers, enhancedDispatch) {
  let store = new Store(initialState, reducers);
  // Override the default dispatch
  store.initialDispatch(enhancedDispatch(store));
  return store;
}

// Receive an array
/ / requirements for function and form the array elements such as: (store) = > (next) = > (action) = > {}
// Store: To enable middleware functions to access the store object
// next: the next middleware function to be nested internally
// action: The call to dispatch(action) requires access to the action
let applyMiddleware = (. middlewareArray) = > (store) = > {
  // All middleware functions are nested in reverse order to generate an enhanced dispatch that integrates all middleware functions
  if (middlewareArray.length) {
    let innerMethod;
    // fn requires a parameter to receive store.dispatch; The return value is a function
    // Since we want to embed the original store.dispatch as part of the fn function body, we require that fn have a parameter that receives store.dispatch and calls it from the appropriate location in its function body
    // Since fn may also be built into the function before it, the return value must be a function
    middlewareArray.forEach(fn= > {
      // In order to access the store object, we need to nest another layer of functions to receive the store
      // Minimal access policy, exposing only getState to middleware functions
      const smStore = {
        getState: store.getState
      }
      if(! innerMethod) innerMethod = fn(smStore)(store.dispatch);else innerMethod = fn(smStore)(innerMethod);
    });
    // The innerMethod is the final dispatch, which is still called with dispatch(Action)
    returninnerMethod; }}Copy the code

Redux implements applyMiddleware

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {... Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (... args) => f(g(h(... args))). */
export default function compose(. funcs) {
  if (funcs.length === 0) {
    return (arg) = > arg
  }

  if (funcs.length === 1) {
    return funcs[0]}return funcs.reduce((a, b) = > (. args) = >a(b(... args))) }// applyMiddleware
export default function applyMiddleware(. middlewares) {
  return (createStore) = > (. args) = > {
    conststore = createStore(... args)let dispatch = () = > {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.')}const middlewareAPI = {
      getState: store.getState,
      dispatch: (. args) = >dispatch(... args), }const chain = middlewares.map((middleware) = >middleware(middlewareAPI)) dispatch = compose(... chain)(store.dispatch)return {
      ...store,
      dispatch,
    }
  }
}
Copy the code

demo

<body>
  <div id="app"></div>
  <button onclick="add()">add</button>
  <button onclick="reduce()">reduce</button>
  <script>

    let obj = {
      count: 0
    };
    let reducers = {
      add: function (state, action) {
        if (action.type === 'add') {
          return Object.assign({}, state, {
            count: state.count + 1})}return state;
      },
      reduce: function (state, action) {
        if (action.type === 'reduce') {
          return Object.assign({}, state, {
            count: state.count - 1})}returnstate; }};// Middleware functions
    let logMiddleware = (store) = > (next) = > (action) = > {
      console.log('log middle ware --- start');
      next(action);
      console.log('log middle ware --- end');
    }
    const loggerMiddleware = (store) = > (next) = > (action) = > {
      console.log('---before state::', store.getState());
      console.log('---doing dispatch type::', action.type);
      next(action);
      console.log('---after state::', store.getState());
      console.log('---------divide--------');
    }

    // Integrate middleware functions
    let enhancedDispatch = applyMiddleware(loggerMiddleware, logMiddleware);

    let store = createStore(obj, reducers, enhancedDispatch);

    store.subscribe(function () {
      console.log(obj === this.state); // true, state is just a change, its original initialState reference remains unchanged
      document.querySelector('#app').textContent = this.state.count;
    });

    function add() {
      store.dispatch({
        type: 'add'})},function reduce() {
      store.dispatch({
        type: 'reduce'})},</script>
</body>
Copy the code