Use the Three Redux principles

  • For a single data source, the entire application can only have one state
  • State is read-only. Changing state can only trigger one action via Dispatch
  • The state was changed using the Reducer pure function

The method of story

  • CreateStore creates a store and returns an object containing methods such as Dispatch, SUBSCRIBE, getState, etc

  • ApplyMiddleware accepts multiple middleware, which we pass through layer by layer at dispatch(Action)

  • Compose merges middleware, compose(middlerware1, middlerware2, middlerware3) eventually returns compose(middlerware1(middlerware2(middlerware3(… args))))

  • BindActionCreator binds dispatch to Action

  • CombineReducers receives reducers cycle to process state


CreateStore method

The createStore method is relatively simple, receiving three parameters reducer, preloadedState, enhancer return an object, the following is the basic use of Redux

import {createStore} from 'redux'; // reduce pure function consttestReduce = (initState = 777, action) => {
    switch (action.type) {
        case 'add':
            return initState += action.payload;
        case 'minus':
            return initState -= action.payload;
        default:
            returninitState; }}; // create actionfunction
const createAction = (type, num) => {
    return {
        type,
        payload: num
    }
}

const store = createStore(testReduce);
console.log('store.getState()', store.getState()); // subscribe returns a method to unsubscribe listening.function test1() {
    console.log('store.getState() test1', store.getState());
})()
store.subscribe(function test2() {
    console.log('store.getState() test2', store.getState()); // 780 778
})
store.dispatch(createAction('add', 3)); // Trigger action store.dispatch(createAction()'minus', 2)); // Triggers the action of minusCopy the code

Let’s take a look at the createStore source code

export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined'Enhancer = preloadedState; enhancer = preloadedState; }if (enhancer) {
    enhancer(createStore)(reducer, preloadedState)
  }

  letCurrentReducer = Reducer // The incoming reducer pure functionletCurrentState = preloadedState // The default state passed inletCurrentListeners = [] // Initialize the listener arrayletNextListeners = currentListeners // Temporary listeners arraylet isDispatching = false// Return the current statefunction getState() {// Determine whether dispatch is performed and whether dispatch is in progressif (isDispatching) {... }
    returnCurrentState} // Add listenser, return a function to cancel listenerfunction subscribe(listener) {
    if(typeof listener ! = ='function') {... }

    if (isDispatching) {... }

    let isSubscribed = true

    nextListeners = currentListeners.slice()
    nextListeners.push(listener)

    return function unsubscribe() {
      if(! isSubscribed) {return
      }

      if (isDispatching) {... }

      isSubscribed = falseNextListeners = currentListeners. Slice () const index = listeners. nextListeners.splice(index, 1) } }functionDispatch (action) {// Determine that the action must be object and must have onetypeThe field ofif(! isPlainObject(action)) {... }if (typeof action.type === 'undefined') {... }

    if (isDispatching) {... }

    try {
      isDispatching = trueCurrentState = currentReducer(currentState, action) } finally { isDispatching =false* * * * * * * * * * * * * * * * Notice how dispatches (actions) trigger listeners to call the subscribe added callbackfor (leti = 0; i < listeners.length; I ++) {const listener = listeners[I] listener()} // return actionreturnAction} // Trigger a unique action, trigger the default option in Reduce, and get the initial state. // Call createStore and get an initial state Dispatch.type: Math.radom() })
  return { getState, dispatch, subscribe }
}
Copy the code

The createStore method is relatively simple and uses our subscribe/publish model


ApplyMiddleware method

The above actions are all synchronous. In the development process, we cannot avoid using asynchronous development. How to use asynchronous action? This time to use our middleware, let’s first look at how to use a middleware:

// Middleware is a higher-order function // const AsyncThunk = ({getState, dispatch}) => next => action => {//if(typeof action === 'function'{/ /return action(dispatch, getState)
//    }
//    returnNext (action) //} // now convert const AsyncThunk = to a normal functionfunction({getState, dispatch}) {
    return function(next) {
        return function(action) {
            if(typeof action === 'function') {
                return action(dispatch, getState)
            }
            returnNext (action)}}} const store = createStore(testReduce, applyMiddleware(AsyncThunk)); // Async action, const asyncAdd = () => dispatch => {setTimeout(() => {
        dispatch(computeAction('add', 7))// Print 785}, 2000)} store.dispatch(asyncAdd())Copy the code

Above is the main source code for middleware use and asynchronous middleware, read in conjunction with applyMiddleware and createStore

const store = createStore(
    testReduce, // applyMiddleware calls applyMiddleware(AsyncThunk) for the first time); // createStore.jsexport default createStore(reducers, preloadState, enhancer) {
    ...
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
        enhancer = preloadedState
        preloadedState = undefined
    }

    if(typeof enhancer ! = ='undefined') {// Enhancer createStore is passed in to return a reducer and preloadedState func and a second or third call to applyMiddlewarereturnenhancer(createStore)(reducer, preloadedState) } ... } // applyMiddleware partial source codeexport default functionapplyMiddleware(... Middlewares) {// createStore is the createStore function passed by the above enhancer for the second time //... Args is the third call to the passed reducers and preloadedStatereturn function (createStore) {
        return function(... Args) {// createStore const store = createStore(... args)letdispatch = () => {... } const middlewareAPI = { getState: store.getState, dispatch:function(... Args) {// The dispatch at this point is equivalent to the wrapped dispatch belowreturndispatch(... args) } }if(middlewares. Length === 1) {// Here the middleware is called twice and returns a function that receives an action, dispatch = middlewares[0](middlewareAPI)(store.dispatch) }return {
                ...store,
                dispatch
            }
        }
    }
}
Copy the code
const asyncAdd = () => dispatch => {
    setTimeout(() => {
        dispatch(computeAction('add', 7))// Print 785}, 2000)} store.dispatch(asyncAdd())Copy the code

This is equivalent to middlewares[0](middlewareAPI)(store.dispatch)(asyncAdd()))

  • And the middleware picks it upaction, judge is a function type, put wrappeddispatch,getStateThe method is passedactionThat’s a function of thetaasyncAddThe function returned after the first call;
  • If it’s normalactionThe middleware will execute after it passes throughnext(action) = dispatch(action)thisdispatchIt’s not packaged

Compose method

The compose higher-order function should be used when using middleware, for example, the compose higher-order function Merge compose(middlerware1, middlerware2, middlerware3) and eventually return compose(middlerware1(middlerware2(middlerware3(… args))))

export default functioncompose(... Funcs) {// The passed middleware length is 0 and one is returnedfunctionThe second call is passed to store.dispatch and returns store.dispatchif (funcs.length === 0) {
        returnArg => arg} // If the middleware length passed in is 0, return the middlewareif (funcs.length === 1) {
        returnFuncs [0]} // The first time b middleware accepts the argument is dispatch, Action => {} // Next (action) => {} // Next (action) = Dispatch (Action) to change statereturn funcs.reduce(function(a, b) {
        return function(... args) {returna(b(... Args))}})} // applyMiddleware will also change to... const chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(... chain)(store.dispatch) // compose(middlerware1(middlerware2(middlerware3(... args)))) ... Const Store = createStore(reducers, applyMiddleware(middlerware1, middlerware2, middlerware3))Copy the code

Middleware3 returns a function that receives the action and sends it to Middleware2’s Next, so the next parameter in our current middleware is the last intermediate Function (action){}, the next argument is an unwrapped dispatch function when there is no previous middleware, that is, when we execute wrapped Dispatches

  • Call themiddleware1thefunction (action){}And perform thenext(action)And themiddleware1thenextismiddleware2thefunction (action){}
  • tomiddleware3whennextIt’s not packageddispatch.next(action) = dispatch(action)So the initialactionSo layer by layer through the middleware to get to our realdispatchYou may get a little confused when you see this, but let’s see a demo:
Const store_dispatch = const store_dispatch =function store_dispatch(action) {
    console.log('action', action);
}
const middleware1 = next => action => {
    console.log(`function name: middleware1\n dispatch: ${next}\n args: ${JSON.stringify(action)}\n`)
    console.log('next.name:'+next.name, ' '.'store_dispatch.name:'+store_dispatch.name);
    console.log('\n\n');
    return next(action)
}
const middleware2 = next => action => {
    console.log(`function name: middleware2\n dispatch: ${next}\n action: ${JSON.stringify(action)}\n`)
    console.log('next.name:'+next.name, ' '.'store_dispatch.name:'+store_dispatch.name);
    console.log('\n\n');
    return next(action)
}
const middleware3 = next => action => {
    console.log(`function name: middleware3\n dispatch: ${next}\n action: ${JSON.stringify(action)}\n`)
    console.log('next.name:'+next.name, ' '.'store_dispatch.name:'+store_dispatch.name);
    console.log('\n\n');
    return// compose(middleware1, middleware2, middleware3) const dispatch = compose(middleware1, middleware2, middleware3)(store_dispatch); dispatch('123')
Copy the code

middleware1->middleware2->middleware3
middleware1
next
middleware2


CombineReducers method

In the case of many states, we cannot and do not want to change all states through a reducer, at this time we used combineReducers

State const happinessReducer = (initState = 0, action) => {switch (action.type) {case 'add_happiness':
            return initState += action.payload;
        default:
            returninitState; }}; State const badThingReducer = (initState = 10, action) => {switch (action.type) {case 'minus_bad_things':
            return initState -= action.payload;
        default:
            returninitState; }}; const store = createStore(combineReducers({ happiness: happinessReducer, badThings: badThingReducer })); console.log(store); console.log('store.getState()', store.getState());

store.subscribe(function test2() {
    console.log('store.getState() test2', store.getState());
})
store.dispatch({
    type: 'add_happiness',
    payload: 99999
});
store.dispatch({
    type: 'minus_bad_things',
    payload: 10
});
Copy the code

The interview is a simple way to use, next we look at the source code:

export default functionCombineReducers (reducers) {const reducerKeys = object. keys(reducers) const finalReducers = {} // Copy the passed reducersfor (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  return functionCombination (State = {}, action) {// Default is state unchangedlet hasChanged = falseConst nextState = {} // Loop all reducer to get the current state and the changed statefor (leti = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey ! == previousStateForKey} // Return the latest nextState if changed otherwise return statereturn hasChanged ? nextState : state
  }
}
Copy the code

CombineReducers function source code or relatively easy to understand, not much BB.


BindActionCreators method

When redux was used in React, we did not want each component to introduce dispatch to call the action. In this case, we used bindActionCreators. Let’s take a look at the basic application.

. // action an action const computeAction = (type, num) => {
    return {
        type,
        payload: num
    }
}

const store = createStore(testReduce);

console.log(store);
console.log('store.getState()', store.getState());

store.subscribe(function test2() {
    console.log('store.getState() test2', store.getState()); }) const actionCreators = {computeAction} // Pass in an actionCreators, and dispatch constbindDispatch = bindActionCreators(actionCreators, store.dispatch);
console.log(bindDispatch) // Call directlybindThe method at Dispatch is finebindDispatch.computeAction('add', 3);
bindDispatch.computeAction('minus', 2);
Copy the code

Let’s take a look at the source code:

function bindActionCreator(ActionCreator, dispatch) {// Return onefunctionPass the parameter transparently to the actionCreator function that is passedreturn function() {
      return dispatch(actionCreator.apply(this, arguments))
    }
  }
  export default function bindActionCreators(ActionCreators, dispatch) {// The passed ActionCreators is a function that goes directly to the Dispatchif (typeof actionCreators === 'function') {
      return bindActionCreator(actionCreators, dispatch)} const boundActionCreators = {} // is the object, loop key, that binds each ActionCreator to a Dispatchfor (const key in actionCreators) {
      const actionCreator = actionCreators[key]
      if (typeof actionCreator === 'function') {
        boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
      }
    }
    return boundActionCreators
  }
  
Copy the code

BindActionCreators is mainly used in the React-Redux application scenario, which enables React to bind dispatch without any sense during connect and does not need to introduce Dispatch for each component.


conclusion

Redux is a state management library that can be used on any framework. Redux is a state management library that can be used on any framework. Redux is a state management library that can be used on any framework. React react react react React React React React