Learn the overall structure of redux source code and deeply understand the principle of redux and its Middleware

Posted by drfate on Mon, 15 Jun 2020 04:06:57 +0200

1. Preface

Hello, I am Ruokawa . This is the eighth part of the whole learning source architecture series. The word "overall architecture" seems to be a little big. Let alone the overall structure of the source code. The main thing is to learn the overall structure of the code without going into the implementation of other specific functions that are not the main line. This article is about the code of the actual warehouse.

If someone said how to read the source code, you can recommend my source series of articles if you are reading the article, that's great.

Learn the overall structure of the source code as follows:

1.Learn the overall architecture of jQuery source code, and build your own js class library
2.Learn the overall architecture of the source code of the anderscore, and build its own functional programming class library
3.Learn the overall architecture of lodash source code, and build your own functional programming class library
4.Learn the overall structure of sentry source code and build your own front-end exception monitoring SDK
5.Learn the overall architecture of the source code of vuex, and build your own state management library
6.Learn the overall architecture of axios source code and create your own request Library
7.Learning the whole structure of koa source code, analyzing the principle of koa onion model and co principle
Interested readers can click to read.

Other source plans include: express,vue-rotuer,redux, react-redux Wait for the source code, I don't know when I can finish (crying), welcome to continue to pay attention to me (ruokawa).

Source class article, general reading is not high. If you have the ability to understand it, you can read it yourself. Don't want to see, dare not see will not go to see the source code.

So my article, try to write to let the readers who want to read the source code and don't know how to read it understand.

Read this article and you will learn:

  1. git subtree management sub warehouse
  2. How to learn redux source code
  3. The principle of redux Middleware
  4. The implementation of redux API
  5. Comparison between vuex and redux
  6. wait

1.1 the best way to read this article

Put my Redux source repository git clone https://github.com/lxchuan12/redux-analysis.git Clone it. By the way, star My redux source learning warehouse Cubic meter ^. With the article rhythm debugging and example code debugging, using chrome hands-on debugging is more impressive. The article does not need to look at a long section of code, you can look at it when debugging. Read this kind of source code article a hundred times, maybe not as good as debugging several times. Also welcome to add me wechat communication ruochuan12.

2. git subtree management sub warehouse

Wrote a lot of source code articles, vuex, axios, koa, etc. are using the new warehouse to clone a source code in their own warehouse.
Although the computer can pull the latest code and see the original author's git information. But after uploading to github. Readers can't see the GIT information of the original warehouse author. So I found the git submodules solution, but it's not very suitable. Later, git subtree was discovered.

Briefly, the difference between npm package and git subtree.
npm package is unidirectional. git subtree is bidirectional.

See this article for details @Deli (the original champion): use Git Subtree to synchronize subprojects in two directions among multiple Git projects, with a concise user manual

After learning git subtree, I created a new redux analysis project and cloned the redux source code 4.x (as of June 13, 2020, the latest version of 4.x branch is 4.0.5, and the master branch is ts, which I don't want some readers who are not familiar with TS to understand temporarily) into a subproject of my project to retain git information.

The corresponding command is:

git subtree add --prefix=redux https://github.com/reduxjs/redux.git 4.x

3. Preparation for debugging redux source code

Before, I answered a question in Zhihu Ruokawa: how can I do if I can't understand the source code of the front-end framework in a year?
Recommended some materials, reading volume is good, you can have a look if you are interested. There are four main points:

1. With the help of debugging

2. Search and consult relevant highly praised articles

3. Record the incomprehensible places and consult relevant documents

4. Summary

It's very important to see the source code debugging, so each of my source code articles describes in detail how to debug the source code.

Breakpoint debugging Essentials:

The assignment statement can be skipped by pressing F10 step by step. Just look at the return value. See later in detail.

To execute a function, you need to press F11 to follow it. You can also use comments and context to push back what the function has done.

Some don't need to look at it, just press F8 to go to the next breakpoint

Refresh re debug press F5

Before debugging the source code, take a brief look at the workflow of redux, and have a general impression.

3.1 generating sourcemap from roll up is easy to debug

modify rollup.config.js File, the output configuration generates the sourcemap.

// redux/rollup.config.js  Some omissions
const sourcemap = {
  sourcemap: true,
};

output: {
    // ...
    ...sourcemap,
}

Installation dependency

git clone http://github.com/lxchuan12/redux-analysis.git
cd redux-analysi/redux
npm i
npm run build
# After compilation, the files in the format of sourcemap. Map will be generated to the dist, es and lib directories.

Take a closer look at the redux/examples directory and redux/README.

At this time, I will create a new folder named examples under the root path, and put the counter Redux / examples / counter vanilla written by the native js/ index.html , copy to examples/index.html . At the same time, copy the packed redux/dist directory containing the sourcemap to the examples/dist directory.

modify index.html Of script redux.js The file is the path in dist.

In order to distinguish and debug subsequent HTML files, I index.html Rename to index. 1 redux.getState.dispatch .html.

# Redux analysis root
# Install npm package for startup service
npm i -g http-server
cd examples
hs -p 5000

You can debug happily. Can clone my project git clone directly http://github.com/lxchuan12/redux-analysis.git . Local debugging, hands-on practice, easy to digest and absorb.

4. Learn redux source code by debugging counter example

Next let's look at examples / index. 1 redux.getState.dispatch . html file. First look at the html section. Just wrote a few button s, which is relatively simple.

<div>
    <p>
    Clicked: <span id="value">0</span> times
    <button id="increment">+</button>
    <button id="decrement">-</button>
    <button id="incrementIfOdd">Increment if odd</button>
    <button id="incrementAsync">Increment async</button>
    </p>
</div>

js part, also relatively simple. A counter function is declared and passed to Redux.createStore(counter) to get the result store, which is an object. The render method renders numbers to the page. use store.subscribe(render) the render method of the subscription. also store.dispatch({type: 'INCREMENT'}) method, calling store.dispatch The render method is triggered. This implements a counter.

function counter(state, action) {
    if (typeof state === 'undefined') {
        return 0
    }

    switch (action.type) {
        case 'INCREMENT':
        return state + 1
        case 'DECREMENT':
        return state - 1
        default:
        return state
    }
}

var store = Redux.createStore(counter)
var valueEl = document.getElementById('value')

function render() {
    valueEl.innerHTML = store.getState().toString()
}
render()
store.subscribe(render)

document.getElementById('increment')
.addEventListener('click', function () {
    store.dispatch({ type: 'INCREMENT' })
})

// Omit some temporary invalid codes

Thinking: after reading this code, where would you interrupt to debug.

// Four breakpoints
// 1.
var store = Redux.createStore(counter)
// 2.
function render() {
valueEl.innerHTML = store.getState().toString()
}
render()
// 3.
store.subscribe(render)
// 4.
store.dispatch({ type: 'INCREMENT' })

Sometimes, the Scope on the right side of the figure needs attention. It will display variables such as closures, global environment, current environment, etc., as well as specific code locations such as functions to help you understand the code.

Breakpoint debugging, press F5 to refresh the page, press F8, and put the mouse on Redux and store.

You can see that there are several methods on Redux. namely:

  • __DO_NOT_USE__ActionTypes: {INIT: "@@redux/INITu.v.d.u.6.r", REPLACE: "@@redux/REPLACEg.u.u.7.c", PROBE_UNKNOWN_ACTION: ƒ}
  • applyMiddleware: the applyMiddleware() function is an enhancer, combining multiple middleware, and finally enhancing store.dispatch Function, when dispatching, all middleware can be executed in series.
  • bindActionCreators: use bindActionCreators (action creators, dispatch) to generate actions, mainly for other libraries, such as react redux.
  • Combinedreducers: combine reducers to combine multiple reducers and return a total reducer function.
  • Compose: compose() combines multiple functions from right to left. For example, compose(f, g, h) finally gets this result (... Args) = > F (g (H (... Args)))
  • createStore: create store (reducer, preloadedstate, enhancer) generates store object

There are several ways to look at the store. namely:

  • Dispatch: to dispatch(action), i.e. to traverse and execute the functions collected by the subscribe in turn
  • Subscribe: the subscribe(listener) subscription collection function exists in the array, waiting for the dispatch to be triggered to execute in turn. Returns a function to unsubscribe from listening.
  • getState: ƒ getState() gets the object with the inner closure of the createStore function.
  • replaceReducer: replaceReducer(nextReducer) is mainly used for redux developer tools to compare the similarities and differences between current and previous operations. It's a bit like time travel.
  • Symbol(observable): ƒ observable()

that is Official documents redux.org.js API on.

We will not go into the implementation of each API for now. Press F5 again to refresh the page, breakpoint to var store= Redux.createStore (counter). Press F11 all the time to go through the main process first.

4.1 Redux.createSotre

The structure of the createStore function is like this. Does it look simple? Finally, it returns the object store, including methods such as dispatch, subscribe, getState and replaceReducer.

// Several codes are omitted
export default function createStore(reducer, preloadedState, enhancer) {
    // Omit parameter check and replace
    // Current reducer function
    let currentReducer = reducer
    // Current state
    let currentState = preloadedState
    // Current listening array function
    let currentListeners = []
    // Next listening array function
    let nextListeners = currentListeners
    // Is it in dispatch
    let isDispatching = false
    function ensureCanMutateNextListeners() {
        if (nextListeners === currentListeners) {
        nextListeners = currentListeners.slice()
        }
    }
    function getState() {
        return currentState
    }
    function subscribe(listener) {}
    function dispatch(action) {}
    function replaceReducer(nextReducer) {}
    function observable() {}
    // ActionTypes.INIT @@redux/INITu.v.d.u.6.r
    dispatch({ type: ActionTypes.INIT })
    return {
        dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
    }
}

4.2 store.dispatch(action)

function dispatch(action) {
    // If the action is an object, if not, an error will be reported
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }
    // judge action.type  Whether it exists, if not, an error will be reported
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }
    // If not, report an error
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
        // Set to false after calling
      isDispatching = false
    }
    //  Take the collected functions out and call them in turn
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    // Finally return to action
    return action
  }
var store = Redux.createStore(counter)

The above sentence has been debugged.

Continue to press F11 for debugging.

function render() {
    valueEl.innerHTML = store.getState().toString()
}
render()

4.3 store.getState()

The implementation of getState function is relatively simple.

function getState() {
    // If it is judged to be in dispatch, an error will be reported
    if (isDispatching) {
        throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
            'The reducer has already received the state as an argument. ' +
            'Pass it down from the top reducer instead of reading it from the store.'
        )
    }
    // Returns the current state
    return currentState
}

4.4 store.subscribe(listener)

Subscribe to the listening function and store it in the array, store.dispatch(action) to traverse the execution.

function subscribe(listener) {
    // Subscription parameter verification is not function error
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }
    // In dispatch, error reported
    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
      )
    }
    // Subscription is true
    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    // Returns a function to unsubscribe
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }
      // In dispatch, an error is reported
      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
        )
      }
      // Subscribe to false
      isSubscribed = false

      ensureCanMutateNextListeners()
    //   Find the current listening function
      const index = nextListeners.indexOf(listener)
    //   Delete in array
      nextListeners.splice(index, 1)
      currentListeners = null
    }
  }

When we get here, we'll finish debugging and learning Redux.createSotre , store.dispatch , store.getState , store.subscribe Source code of.

Next, we write a middleware example to debug the middleware related source code.

5. Redux middleware related source code

Middleware is the key, and interviewers often ask such questions.

5.1 Redux.applyMiddleware(...middlewares)

5.1.1 preparing logger example debugging

For debugging Redux.applyMiddleware (... Middlewars), I'm in examples/js/middlewares.logger.example.js to write a simple logger example. There are three logger1, logger2 and logger3 functions respectively. Because they are similar, I only show logger1 function here.

// examples/js/middlewares.logger.example.js
function logger1({ getState }) {
  return next => action => {
      console.log('will dispatch--1--next, action:', next, action)

      // Call the next dispatch method in the middleware chain.
      const returnValue = next(action)

      console.log('state after dispatch--1', getState())

      // This will likely be the action itself, unless
      // a middleware further in chain changed it.
      return returnValue
  }
}
// Omit logger2 and logger3

The logger middleware function is also relatively simple to do. It returns two-tier functions. Next is the next middleware function, and the call returns the result. In order to make readers understand, I use arrow function for logger1 and ordinary function for logger2.

After writing the example, let's go on to see how to debug Redux.applyMiddleware (... Middlewars)) source code.

cd redux-analysis && hs -p 5000
# As mentioned above, NPM i-g HTTP server

open http://localhost:5000/examples/index.2.redux.applyMiddleware.compose.html, press F12 to open the console,

First click the plus sign operation + 1 to display the result.

As you can see from the figure, next is the next function. First 1-2-3, then 3-2-1.

This is what we often call middleware, aspect oriented programming (AOP).

Next, debug the following statements and make breakpoints where you think it is important.

// examples/index.2.redux.applyMiddleware.compose.html
var store = Redux.createStore(counter, Redux.applyMiddleware(logger1, logger2,  logger3))

5.1.2 Redux.applyMiddleware (... Middlewars) source code

// redux/src/applyMiddleware.js
/**
 * ...
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */
export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = 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
    }
  }
}
// redux/src/createStore.js
export default function createStore(reducer, preloadedState, enhancer) {
  // Omit parameter verification
  // If the second parameter 'preloadedState' is a function and the third parameter 'enhancer' is undefined, swap them.
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // enhancer is` Redux.applyMiddleware `Function returned
    // The args of createStore is' reducer, preloadedState`
    /**
     * createStore => (...args) => {
            const store = createStore(...args)
            return {
              ...store,
               dispatch,
            }
        }
     ** /
    // Finally, the enhanced store object is returned.
    return enhancer(createStore)(reducer, preloadedState)
  }
  // Omit subsequent code
}

Put the received middleware functions logger1, logger2 and logger3 into the middlewars array. Redux.applyMiddleware Finally, two-level functions are returned.
The middleware functions are mixed with the parameters getState and dispatch.

// examples/index.2.redux.applyMiddleware.compose.html
var store = Redux.createStore(counter, Redux.applyMiddleware(logger1, logger2,  logger3))

The last sentence is actually to return a store object with enhanced dispatch.

The enhanced dispatch function uses the Redux.compose(...functions) are executed in series.

5.2 Redux.compose(...functions)

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.js
dispatch = compose(...chain)(store.dispatch)
// compose
funcs.reduce((a, b) => (...args) => a(b(...args)))

These two sentences may not be so easy to understand. You can debug breakpoints several times. I convert the arrow function to a normal function.

funcs.reduce(function(a, b){
  return function(...args){
    return a(b(...args));
  };
});

In fact, the comments in the redux source code are very clear. There is a bunch of comments above the compose function, including one sentence: combine multiple functions, from right to left, for example, compose(f, g, h) and finally get the result (... Args) = > F (g (H (... Args))

5.2.1 compose function evolution

see Redux.compose(...functions) function source code, or do not understand, do not hurry do not panic, after eating eggs and soup. Take a closer look at how it evolved. Let's briefly look at the following requirements.

Pass in a value, multiply it by 10, add 10, and subtract 2.

It's easy to implement.

const calc = (num) => num * 10 + 10 - 2;
calc(10); // 108

But there is a problem in this way. It's not easy to expand. For example, when I want to multiply by 10, I will print out the result.
For the sake of extension, we write three functions separately.

const multiply = (x) => {
   const result = x * 10;
   console.log(result);
   return result;
};
const add = (y) => y + 10;
const minus = (z) => z - 2;

// Calculation results
console.log(minus(add(multiply(10))));
// 100
// 108
// In this way, we can calculate the results of three functions.

Then we implement a relatively general function to calculate the results of these three functions.

const compose = (f, g, h) => {
  return function(x){
    return f(g(h(x)));
  }
}
const calc = compose(minus, add, multiply);
console.log(calc(10));
// 100
// 108

There is still a problem. Only three functions are supported. I want to support multiple functions.
We have learned that the reduce method of arrays can achieve such a function.
Previous function

// We often use reduce to calculate the sum of numerical arrays
[1,2,3,4,5].reduce((pre, item, index, arr) => {
  console.log('(pre, item, index, arr)', pre, item, index, arr);
  // (pre, item, index, arr) 1 2 1 (5) [1, 2, 3, 4, 5]
  // (pre, item, index, arr) 3 3 2 (5) [1, 2, 3, 4, 5]
  // (pre, item, index, arr) 6 4 3 (5) [1, 2, 3, 4, 5]
  // (pre, item, index, arr) 10 5 4 (5) [1, 2, 3, 4, 5]
  return pre + item;
});
// 15

pre is the last return value, where is the value 1,3,6,10. In the next example, anonymous functions.

function(x){
  return a(b(x));
}

item is 2, 3, 4, 5, in the next example, minus, add, multiply.

const compose = (...funcs) => {
  return funcs.reduce((a, b) => {
    return function(x){
      return a(b(x));
    }
  })
}
const calc = compose(minus, add, multiply);
console.log(calc(10));
// 100
// 108

and Redux.compose(...functions) in fact, it's just that middleware returns double-layer functions.

So what is returned is the next function, which is executed in strings, forming the onion model of middleware.
People say a picture is worth a thousand words. I drew a relatively simple schematic diagram of redux middleware.

If it is not clear, it is recommended to follow the example given by me to debug more.

cd redux-analysis && hs -p 5000
# As mentioned above, NPM i-g HTTP server

open http://localhost:5000/examples/index.3.html, press F12 to open the console debugging.

5.2.2 implementation of the compose function of the front-end framework

The implementation of the compose function in the source code of lodash is similar to the reduction of array, but only the internal implementation of arrayReduce

Self reference article: learning the whole architecture of lodash source code

// lodash source code
function baseWrapperValue(value, actions) {
    var result = value;
    // If it is an instance of lazyWrapper, call LazyWrapper.prototype.value  Method, that is, lazyValue method
    if (result instanceof LazyWrapper) {
        result = result.value();
    }
    // Similar to []. reduce(), passing the return result of the previous function as a parameter to the next function
    return arrayReduce(actions, function(result, action) {
        return action.func.apply(action.thisArg, arrayPush([result], action.args));
    }, result);
}

The koa compose source code also has the implementation of the compose function. The implementation is loop plus promise.
Because of the long code, I omitted it. See the link for details Ruochuan: learn the overall structure of koa source code, and analyze the principle of koa onion model and co principle Section koa compose source code (onion model implementation)

6. Redux.combineReducers(reducers)

open http://localhost:5000/examples/index.4.html, press F12 to open the console, and then debug the following Redux.combineReducers(reducers) and Redux.bindActionCreators(actionCreators, dispatch) implementation. Since the article is long, these two functions are not explained in detail.

The combinedreducers function is simply to merge multiple reducers into a function combination.

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    // Omit some development environment judgment code

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }

  // After some processing, we get the final reducerkeys
  const finalReducerKeys = Object.keys(finalReducers)

  // Omit some development environment judgment code

  return function combination(state = {}, action) {
    // Omit some judgments of development environment

   // Record whether the state has been modified before and after using the hasChanged variable
    let hasChanged = false
    // Declare objects to store the next state
    const nextState = {}
    //Traverse finalReducerKeys
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      // Execute reducer
      const nextStateForKey = reducer(previousStateForKey, action)

      // Omit fault tolerant code

      nextState[key] = nextStateForKey
      // If the two key comparisons are not equal, they will change
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // The last keys array will change if the comparison is not equal
    hasChanged =
      hasChanged || finalReducerKeys.length !== Object.keys(state).length
    return hasChanged ? nextState : state
  }
}

7. Redux.bindActionCreators(actionCreators, dispatch)

If the first argument is a function, it returns a function directly. If it is an object, the assignment is traversed and the boundActionCreators object is finally generated.

function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  // ... omit some fault-tolerant judgments

  const boundActionCreators = {}
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

In addition to the API provided by redux store.replaceReducer (nextdeducer) didn't analyze it. Everything else was analyzed.

8. Simple comparison between vuex and redux

8.1 source code implementation form

From the source implementation point of view, vuex source mainly uses constructors, while redux is multi-functional programming and closure.

8.2 coupling degree

Because of the strong coupling between vuex and vue, it can not be used without vue. redux has nothing to do with react, so it can be used in applets or jQuery. If it needs to be used with react, it also needs to combine react redux library.

8.3 extension

// The logger plug-in is omitted
function logger (store) {
  console.log('store', store);
}
// Passed in as array
new Vuex.Store({
  state,
  getters,
  actions,
  mutations,
  plugins: process.env.NODE_ENV !== 'production'
    ? [logger]
    : []
})
// The execution part of the source plug-in of vuex
class Store{
  constructor(){
    // Pass the entire store object of the instance object of vuex to the plug-in for use
    plugins.forEach(plugin => plugin(this))
  }
}

vuex implementation extension is in the form of plug-ins, while redux is in the form of middleware. The middleware of redux is AOP (aspect oriented programming). In redux Redux.applyMiddleware() is also an enhancement function, so it can also be implemented by users redux ecology Relatively prosperous.

8.4 ease of use

Relatively speaking, vuex is relatively simple to start with, and redux is relatively difficult. redux involves some functional programming, high-order functions, pure functions and other concepts.

9. Summary

This article mainly talks about the specific implementation of redux source code step by step through the way of debugging step by step. Designed to teach readers to debug source code, not afraid of source code.

Interviewers often like to write a redux middleware and talk about the principle of redux middleware.

function logger1({ getState }) {
  return next => action => {
      const returnValue = next(action)
      return returnValue
  }
}
const compose = (...funcs) => {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  // Arrow function
  // return funcs.reduce((a, b) => (...args) => a(b(...args)))
  return funcs.reduce((a, b) => {
    return function(x){
      return a(b(x));
    }
  })
}
const enhancerStore = Redux.create(reducer, Redux.applyMiddleware(logger1, ...))
enhancerStore.dispatch(action)

User triggered enhancerStore.dispatch(action) is enhanced. In fact, it is the first middleware function. The next in the middle is the next middleware function. At last, the next is not enhanced store.dispatch(action).

Finally, let's see a redux workflow chart

Is that more understanding.

If readers find something wrong or can be improved, or where they don't write clearly, please comment. In addition, I think it's well written and helpful for you. You can like it, comment, forward and share it. It's also a kind of support for me. Thank you very much. If someone said how to read the source code, you can recommend my source series of articles if you are reading the article, that's great.

Recommended reading

@Hu dadaha: hands on implementation of Redux (1): gracefully modify the sharing state , a total of 6 sections, very recommended, although I read "react small book" a long time ago, and now I can read it again and have harvest

Meituan @ Yingying Redux from design to source , meituan this is what I saw after I basically wrote the article. I think it's very well written. It's highly recommended

redux Chinese document

redux English document

Ruochuan's learning redux source warehouse

Another series

Interviewer: Inheritance of JS

Interviewer: JS's this point

Interviewer asked: can we simulate the call and apply methods of JS

Interviewer: can you simulate the bind method of JS

Interviewer: can we simulate the new operator of JS

about

Author: he often wanders in the Jianghu under the name of Ruochuan. On the front road, PPT enthusiasts know little, only good at learning.

Ruokawa's blog , reconstructed with vuepress, the reading experience may be better

Nuggets column , welcome to~

Segment fault front view column , welcome to~

Zhihu front end vision column , welcome to~

Column of front-end vision of YuQue , a new column of language sparrow, welcome to pay attention~

github blog , relevant source code and resources are put here to find a star^_ ^

Welcome to WeChat exchange WeChat official account.

Perhaps the more interesting WeChat official account is long. Welcome to add our wechat ruochuan12 (indicate the source, and you can get access to the [front-end vision exchange group] for long-term exchange and learning~

Topics: Javascript git npm Programming React