Knowledge points of reason 2 (redux)

Posted by mortona on Wed, 24 Nov 2021 16:24:31 +0100

mention redux The well-known third-party state management library is also one of the source codes that many small partners will inevitably conquer on the way to advanced development. Redux except and   React   In addition to being used together, it also supports other interface libraries. It is small and vigorous (only 2kB, including dependence).

Today, let's talk about the key things to learn in redux learning;

Three principles

Redux can be described by these three basic principles:

  • Single data source

    Overall application   state   It is stored in an object tree, and the object tree exists only in one   store   Yes.

  • State is read-only

    The only way to change the state is to trigger   action , action is a common object used to describe an event that has occurred.

  • Use pure functions to perform modifications

    To describe how an action changes a state tree, you need to write   reducers.

use

In fact, redux can be used in the following steps:

Declare store file (store state)

// store.js
import { createStore } from 'redux';
import reducer from "./reducer.js";
const store = createStore(reducer);
export default store;

Declare the reducer.js file (modify state)

The declared reducer file is mainly used to modify the saved state

export default const counter = (state = 0, action) => {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state;
  }
};

dispatch triggers action action, subscribe and unsubscribe

store.dispatch({ type: 'INCREMENT' });
const unsubscribe = store.subscribe(()=> {});

Knowledge points

The above is the basis we need to understand. In fact, there is no mystery when redux is spread out. It is a third-party file that saves the state and triggers modification with specific action s. The real core we need to understand is redux's processing of parameter enhancer in createStore and the implementation of applymeddleware, which is the core of redux;

enhancer

Enhancer is translated as an enhancer. Its enhanced object is dispatch, which allows us to change the dispach. In the source code, we can see:


Judge in line 87. If enhancer exists and is a function, createStore will return a Kerry function executed by enhancer in advance in line 96;

Because enhancer needs to enhance the dispatch, enhancer needs createStore as a parameter, and it needs to call dispatch during the enhancement process, so it needs reduer and preloadedState. As for coritization, it is more for the convenience of combination!

The result of the enhancer function is actually the new store and dispatch returned by the applyMiddleware function after aggregating the middleware;
Source code: ad locum

applyMiddleware

Next, let's take a look at what the middleware function does, how it aggregates the middleware and returns a new store, and how it enhances dispatch!
The source code is written in ts, which seems troublesome for some people. I change it to js and remove the redundant throw.
The following is the general framework of the applyMiddleware function. First, we need to take out the original store and dispatch from the parameters passed in by enhancer, enhance the new dispatch through a series of operations, and return the new store and dispatch

export default const applyMiddleware = (...middleware) => {
  return createStore => (reducer, preloadedState) => {
    let store =  createStore(reducer, preloadedState)
    let dispach = store.dispach
    // todo enhanced dispatch
    return {...store, dispach}
  }
} 

We need to execute all middleware functions to enhance the dispatch, because the middleware function also needs to process the dispatch and access the state value of the state warehouse, which means that we need to provide the middleware with redux control.

For example, when we use redux logger to print, we print the status value in the state library. If we can't get the control of redux, we can't access the state value of the store

In fact, the so-called control rights are getStore and dispatch;
So we do this by declaring a middleware API to receive the control of the state warehouse. We only need to pass it to the middleware function in the form of parameters, which is roughly as follows;
+Add logic for

export default const applyMiddleware = (...middleware) => {
  return createStore => (reducer, preloadedState) => {
    let store =  createStore(reducer, preloadedState)
    let dispach = store.dispach
    // todo enhanced dispatch
    // Declare the control circle object of the state warehouse
    + const middlewareAPI = {
    +    getState: store.getState,
    +    dispatch: (action, ...args) => dispatch(action, ...args)
    +  }
    // The aggregation middleware consists of multiple middleware. We traverse and execute each middleware and pass in the control object as a parameter.
    + const chain = middlewares.map(middleware => middleware(middlewareAPI));
    // The returned chain is a new array, which is composed of each middleware function that gets control
    
    // A compose file is used in the source code to deal with the aggregation of middleware
    // In order to reduce the burden on users, it is impossible for users to manually execute every middleware, so we choose to use compose aggregation for execution, so that users can directly get the final results.
    + dispatch = compose(...chain)(store.dispatch)

    return {...store, dispach}
  }
} 

Careful partners should notice a problem, that is, the getState in our warehouse control object is taken directly from the store, but the dispatch is encapsulated again. What is the reason?
Let's explain here that we want to use the enhanced dispatch because it can handle some problems. If we want to get it in the store like getStore, I still pass on the old dispatch, so the enhancement is meaningless. Therefore, we want to use the context dispatch, that is, the enhanced dispatch, So we need to encapsulate it here.
Please move the source code here

compose

export default function compose(...funcs) {
  if (funcs.length === 0) {
    // When there is no middleware, but there are parameters, directly declare the return parameters of a function
    return arg => arg
  }

  // When there is only one middleware, avoid using reduce and directly return the only middleware function
  if (funcs.length === 1) {
    return funcs[0]
  }
 
  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  )
}

Source code in here
The core code of compose is really only a little bit, which is used to execute all Middleware in a nested form and return the final results. Similar to fn1(fn2(fn3(params)))
If you don't understand, you can move to another blog, [[multiple implementation methods of function composition]!]( https://juejin.cn/post/700211...)
As for combineReducers and bindActionCreators, I won't repeat them. I can understand them by myself. It's a simple state of merging store s.

Topics: redux