React-redux is one of the more popular (if not the most popular) state management methods in the React ecosystem. Redux, which relies on redux, inherited the mantle of Flux and introduced three principles: single data source, read-only state, and pure function modification only. An example of the simplest redux application:

// https://cn.redux.js.org/
import { createStore } from 'redux'

/** * This is a reducer pure function of the form (state, action) => state. * describes how an action converts state to the next state. The form of * * state is up to you. It can be primitive types, arrays, objects, *, or even immutable.js generated data structures. The only important point is that * you need to return the brand new object when state changes, not modify the parameters passed in. * * The following example uses the 'switch' statement and strings to do this, but you can write helper classes * depending on different conventions (such as method mappings) that apply to your project. * /
function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

// Create a Redux store to store the state of the application.
The API is {subscribe, dispatch, getState}.
let store = createStore(counter)

// Updates can be subscribed manually, or events can be bound to the view layer.
store.subscribe((a)= > console.log(store.getState()))

// The only way to change internal state is to dispatch an action.
// Actions can be serialized, logged and stored, and later executed in playback mode
store.dispatch({ type: 'INCREMENT' })
/ / 1
store.dispatch({ type: 'INCREMENT' })
/ / 2
store.dispatch({ type: 'DECREMENT' })
/ / 1
Copy the code

CreateStore is the most basic API in Redux. It returns a store. The store has three methods — Dispatch, Subscribe, and getState. In fact, from the method name, we can roughly infer that Redux applies the subscription publishing model; This pattern can be applied directly to the following template code:

/ /
// createStore is a closure that maintains currentState, private listeners;
// the outer layer can only getState through getState;
// Listeners cannot be modified from the outer layer
function createStore(reducer, preloadedState) {
  // Each time subscribe is called, a listener is pushed to the Listeners array;
  // The functions in the Listeners array are executed each time the Dispatches are called;
  const listeners = [];
  let currentReducer = reducer;
  let currentState = preloadedState;

  function dispatch(action) {
    As shown in the sample code, the reducer function takes state and action
    // Action is a simple object with a type attribute;
    // Reducer perform corresponding operations based on type and return a new state.
    currentState = currentReducer(currentState, action);
    // Executes all functions in the Listeners array
    listeners.forEach(listener= > listener());
  }

  function subscribe(listener) {
    listeners.push(listener);
    // Returns an unsubscribe function
    return function unsubscribe() {
      let index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    };
  }

  // This function returns state
  function getState() {
    return currentState;
  }

  return {
    dispatch,
    subscribe,
    getState
  };
}

function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      returnstate; }}// Test it
let store = createStore(counter);
store.subscribe((a)= > console.log(store.getState()));
store.dispatch({ type: 'INCREMENT' });
/ / 1
store.dispatch({ type: 'INCREMENT' });
/ / 2
store.dispatch({ type: 'DECREMENT' });
/ / 1
Copy the code

The above code is intended to provide a general explanation of how Redux works, but there are a number of problems. For example, an action can only be a simple object, but there is no detection in our code. Let’s forget about that for a moment and implement the features first. Next, modify Dispatch to support function arguments:

function dispatch(action) {
  // If a function is passed in, the function is injected with the dispatch and getState methods,
  // And execute the action
  if (typeof action === 'function') {
    return action(dispatch, getState);
  }
  As shown in the sample code, the reducer function takes state and action
  // Action is a simple object with a type attribute;
  // Reducer perform corresponding operations based on type and return a new state.
  currentState = currentReducer(currentState, action);
  // Executes all functions in the Listeners array
  listeners.forEach(listener= > listener());
  return action;
}
Copy the code

Test this out asynchronously:

function counter(state = 0, action) {
 switch (action.type) {
   case 'INCREMENT':
     return state + 1;
   case 'DECREMENT':
     return state - 1;
   default:
     returnstate; }}function asyncAdd(dispatch) {
 return new Promise((resolve, reject) = > {
   setTimeout((a)= > {
     resolve(
       dispatch({
         type: 'INCREMENT'})); },1000);
 });
}
// Test it
let store = createStore(counter);
store.subscribe((a)= > console.log(store.getState()));
store.dispatch({ type: 'INCREMENT' });
/ / 1
store.dispatch({ type: 'INCREMENT' });
/ / 2
store.dispatch(asyncAdd).then((a)= > store.dispatch(asyncAdd)); // 3 after 1s, 4 after 2s
Copy the code

Our code now has redux + Redux-thunk functionality, and no new apis added. But we’ve changed Dispatch in an intrusive way, so what if we need to add other new features? Continue to modify dispatch? Obviously you can’t do that. Redux uses middleware mechanisms to modify dispatch:

function compose(. funcs) {
  if (funcs.length === 0) {
    return args= > args;
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  return funcs.reduce((a, b) = >(... args) => a(b(... args))); }function applyMiddleware(. middlewares) {
  return createStore= >(... args) => {conststore = createStore(... args);let dispatch = (a)= > {
      throw new Error('not valid');
    };

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (. args) = >dispatch(... args) };const chain = middlewares.map(middleware= > middleware(middlewareAPI));
    // applyMiddleware essentially needs to override the old dispatch methoddispatch = compose(... chain)(store.dispatch);return {
      ...store,
      dispatch
    };
  };
}

// 我们的 thunk
const thunk = ({ dispatch, getState }) = > next => action= > {

  // This code is the same as the method we used above to violently modify dispath
  if (typeof action === 'function') {
    return action(dispatch, getState);
  }

  return next(action);
};
Copy the code

Redux-thunk redux-Thunk Redux-Thunk Redux-Thunk Redux I also recommend that you take a look at the redux source code, which is also very short and fully annotated.