Let’s start with a brief explanation of what redux does.

A reducer has two states (on or off), which provides a switch to control whether the reducer is on or off. If you press down, the reducer is on, and if you press up, the reducer is off (defined by action.type, or the reducer can be the opposite). Then the person can choose to press up or dispatch the light to turn it on or off

State: Value that may change (state, i.e., variable, constant)

Reducer: modifies the state state

Action: The convention is an object containing type and Paylaod.

Dispatch: An incoming action triggers the reducer and determines which operation to execute in the reducer based on the action.type

Execution process: For example: Dispatch (action) = Dispatch ({type: “”, payload: {}}) -> Execute reducer() -> Modify state.

So let’s do the above example

There are two reducers. The default state of countReducer is 0, and the default state of sumReducer is 1

And both countReducer’s action.type and sumReducer’s action.type have either INCREMENT or DECREMENT,

Use combineReducers to combine two Reducers and pass the returned functions combinefn into the createStore to return the required stores.

State is the default value given to the reducer parameter

 import {  createStore, combineReducers } from 'redux';
 ​
 function countReducer(state = 0, action) {
     switch (action.type) {
         case 'INCREMENT':
           return state + 1;
         case 'DECREMENT':
           return state - 1;
         default:
           return state;
     }
 }
 ​
 function sumReducer(state = 1, action) {
     switch (action.type) {
         case 'INCREMENT':
           return state + 1;
         case 'DECREMENT':
           return state - 1;
         default:
           return state;
     }
 }
 ​
 const combinefn = combineReducers({ countReducer, sumReducer})
 const  store =  createStore(combinefn);
 ​
 export default store

Copy the code

combineReducers

The source code is as follows:

export default function combineReducers(reducers) { const reducerKeys = Object.keys(reducers) const finalReducers = {} for (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 function combination( state= {}, action ) { let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const actionType = action && action.type throw new Error('more error') } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey ! == previousStateForKey } hasChanged = hasChanged || finalReducerKeys.length ! == Object.keys(state).length return hasChanged ? NextState: state}}reducers: in this case, {countReducer, sumReducer}.Copy the code
  1. First, eliminate the items whose values are not functions in the reducers object and assign them tofinalReducers
  2. Then remove all the keys of reducers and save tofinalReducerKeys
  3. The last returncombinationFunction as a functioncreateStoreThe parameters of the

Combination is the reducer because it is a method used to modify the state state. In order to confuse the reducer originally passed in, we use combination to represent this reducer

In this example: finalReducers = {countReducer: countReducer, sumReducer: sumReducer } finalReducerKeys = [‘countReducer’, ‘sumReducer’]

finalReducerswithfinalReducerKeysWill be used in combination (again below)

Combination Triggered when dispatch({type: “}) traverses all the reducers, executing all the reducers whose logic meets type to change state. Such as:

Return state + 1; Modify the state of

dispatch({type: 'INCREMENT'}) function sumReducer(state = 1, action) { switch (action.type) { case 'INCREMENT': return state + 1; }}Copy the code

createStore

  1. reducer (Function): use thecombineReducersThe correspondence is 0combinationFunction, if not used, the corresponding single is passed inreducerfunction
  2. [preloadedState] (any): Indicates the initial state.

    If you are using[combineReducers](https://link.zhihu.com/?target=https%3A//www.redux.org.cn/docs/api/combineReducers.html)createreducer, preloadedState is finally executed as the firstcombinationIs passed in. By default, it iscombinationState ={}, so it must be a normal object and keep the same structure as the object keys passed by combineReducers. If not, it will throw an error.(Expect the arguments to be the same as the object, keys, passed by combineReducers)

    In an isomorphic application, you can decide whether to send a state hydrate from the server to it, or restore one from a previously saved user session to it.
  3. enhancer (Function): Store enhancer is a higher-order function combining Store Creator that returns a new enhanced Store Creator. One simple role is supported middleware.

    Specific action referenceThe third parameter of createStore also does this , Redux source code read glint, the third parameter of createStore

Here, the reducer value is assigned to currentReducer, that is, the combination value is assigned to currentReducer

Export default function createStore(reducer, preloadedState, enhancer) { Let currentState = preloadedState let currentState = reducreducdState let = reducreducdState () Let isDispatching = false. We are dispatching = false. We are dispatching = false function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } function getState() { return currentState } function subscribe(listener) {} function dispatch(action) {} function replaceReducer(nextReducer) {} function observable() {} dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }Copy the code

That is, the final store is an object with the following properties:

 {
     dispatch,
     subscribe,
     getState,
     replaceReducer,
     [$$observable]: observable
 }

Copy the code

Now let’s look at the functions that correspond to these properties

Some functions in createStore

dispatch

What it does: It updates data through Dispatch,

First dispatch internally when createStore is executed ({type: Actiontypes.init}) Execute currentReducer to initialize the state. (currentReducer is a combination of combineReducers.)

** note: ** in addition to running a currentReducer to calculate the state, we also run a loop to determine the listeners, the currentListeners, and the currentListeners. (We’ll continue with listeners, but keep in mind)

The source code is as follows:

Again, currentReducer is the combination function mentioned above,

// currentState = createStore second argument, and not function type, otherwise 'enhancer' function dispatch(action: A) {try {istreducer = true // combination currentState = currentReducer(currentState, } finally {isListeners = false} const Listeners = (currentListeners = nextListeners) for  (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action }Copy the code

Paste the combination code again:

// finalReducers = {// countReducer: countReducer, // reduce sumReducer: sumReducer // } // finalReducerKeys = ['countReducer', 'sumReducer'] return function combination( state= {}, action ) { let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const actionType = action && action.type throw new Error('more error') } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey ! == previousStateForKey } hasChanged = hasChanged || finalReducerKeys.length ! == Object.keys(state).length return hasChanged ? nextState : state }Copy the code

Each time dispatch is executed, this function is executed, the last state and the current dispatch action are passed, finalReducerKeys are traversed, and all reducers are executed ina loop.

If action. Type is the same in different reducer files, they will all be executed. Unexpected events may occur, such as: CountReducer () and sumReducer () each action has INCREMENT, so the state of both action reducer and countReducer changes.

dispatch({type: 'INCREMENT'}) function countReducer(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1; } } function sumReducer(state = 1, action) { switch (action.type) { case 'INCREMENT': return state + 1; }}Copy the code

That is, after the first internal dispatch({type: actiontypes.init}), the value of the entire state tree is as follows.

 currentState = {
     countReducer: 0,
     sumReducer: 1
 }

Copy the code

getState

Function: Obtain the value from store.getState()

Return currentState

   function getState(){
     return currentState
   }

Copy the code

Stage summary

At this point, the state tree is defined and can be modified, and is traversed through the Listeners in the Dispatch function. We’ve already mentioned that functions in Listeners are created by subscribing, so let’s take a look at the implementation of subscribe

subscribe

React is used as follows:

Store. Subscribe (subscribe) Puts Render in subscribe, that is, every time dispatch changes state, the entire App will be rerendered, which is also consistent with react’s execution mechanism.

import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import store from './redux/index'; const render = () => ReactDOM.render( <App store={ store } />, document.getElementById('root') ); render(); Const unsubscribe = store.subscribe(render) // unsubscribe(Copy the code

Add a function to an array and return an unsubscribe, which removes the function from the array and then runs through the currentListeners array at Dispatch.

   let currentListeners = []
   let nextListeners = currentListeners;
   
   function ensureCanMutateNextListeners() {
     if (nextListeners === currentListeners) {
       nextListeners = currentListeners.slice()
     }
   }
 ​
 function subscribe(listener: () => void) {
     
     let isSubscribed = true
     ensureCanMutateNextListeners()
     nextListeners.push(listener)
     return function unsubscribe() {
       isSubscribed = false
       ensureCanMutateNextListeners()
       const index = nextListeners.indexOf(listener)
       nextListeners.splice(index, 1)
       currentListeners = null
     }
   }
 ​

Copy the code

replaceReducer

Generally, combineReducers is used to combine all reducers to initialize the entire state tree.

However, when using routing, we want to combine the reducer and state required by the page after it is loaded.

CombineReducers is used to combine the new reducer with the existing reducer,

CombineReducers returns a combination function,

In createStore, we assigned currentReducer to this combination. And using currentReducer in dispatch to recalculate state,

CombineReducers after merging the new Reducer, still returns the combination function

So now we need to replace the old currentReducer with replaceReduce and perform a dispatch to recalculate the entire state tree.

The reducer and state of the new page are added to the state tree

   function replaceReduce(nextReducer) {
     currentReducer = nextReducer
     dispatch({ type: ActionTypes.REPLACE } as A)
     return store
   }

Copy the code

This section does not provide an example. For details about how to use the example, see dynamic injection Reducer of the Redux Store

Redux some other functions

bindactioncreators

This function is a utility function. React+Redux bindactioncreators

compose

applyMiddleware

These two functions are used for middleware and are described in a separate article

Refer to the link

Redux official website documentation

Dynamic injection reducer of the Redux store

React+Redux bindactioncreators

Sichuan elder brother – learn redux source code overall architecture, in-depth understanding of Redux and its middleware principle