redux source code analysis and use

Reference document https://cn.redux.js.org/docs/introduction/Motivation.html
Redux is for JavaScript applications can predict the state of the container, data sharing in the project, prior to use redux, look at the model diagram
Here Insert Picture Description
First we some need to understand the composition of the object model Redux: Action, the reducer, Store .
action : The official explanation is that action is to use data from the payload reached the store, which is the only source of store data; to change state through local or remote components, you need to distribute a single action;
the reducer : action sent out to do something the request, just described do something, and not to change the state to update the interface, reducer is to deal with different events according to the action of the type;
store : store action and reducer is to link together objects, the store is essentially is a state tree, save the state of all objects. Any component can directly access a specific object from the store state.

In Redux, all data (for example state) are stored in a container in a store is called in an application store only one object. When a store receives an action, this action will proxy to the relevant reducer. reducer is a pure function, which lets you view the state before, to perform an action and return to a new state.

Workflow :
The state consolidated under a single state in the store to manage this state, this store is created by the reducer, the role reducer is state before accepting returns a new state. The only way to change the state of external dispatch method by calling the store, and a trigger action, the corresponding action is reducer process, then complete the update state. You can add a listener function on the store through subscribe, when store in dispatch method is called, it will monitor the implementation of this function. You can add middleware (Middleware is doing behind us say)

In this workflow, redux desirable to provide the function of:
1 - Create store, namely: createstore ()
2- combined into a plurality of reducer reducer, namely: combineReducers ()
3- created out of the store to provide subscribe, dispatch, getState these methods.
4- application middleware, i.e. applyMiddleware ()

createStore implementation

function createStore(reducer, preloadedState, enhancer) {
    if(enhancer是有效的){  //这个我们后面再解释,现在可以先不管
        return enhancer(createStore)(reducer, preloadedState)
    }
    let currentReducer = reducer // 当前store中的reducer
    let currentState = preloadedState // 当前store中存储的状态
    let currentListeners = [] // 当前store中放置的监听函数
    let nextListeners = currentListeners //下一次dispatch时的监听函数
    //注意:当我们新添加一个监听函数时,只会在下一次dispatch的时候生效。
    // 获取state
    function getState() {
        //...
    }
    // 添加一个监听函数,每当dispatch被调用的时候都会执行这个监听函数
    function subscribe() {
        //...
    }
    // 触发了一个action,因此我们调用reducer,得到的新的state,并且执行所有添加到store中的监听函数。
    function dispatch() {
        //...
    }
    //...
    //dispatch一个用于初始化的action,相当于调用一次reducer
    //然后将reducer中的子reducer的初始值也获取到
    //详见下面reducer的实现。
    return {
        dispatch,
        subscribe,
        getState,
        //下面两个是主要面向库开发者的方法,暂时先忽略
        //replaceReducer,
        //observable
    }
}

As can be seen, createStore method creates a store, but did not directly state this store's return to state, but returns a series of methods, external state can get through some of these methods (getState), or indirectly (by calling the dispatch ) to change state. As for the state it was in existence for closure, but the need for reducer call createStore method, let us consider the role of the reducer

combineReducers
before understanding combineReducers, let's think about the function of reducer: reducer to accept an old and a state action, when this action is triggered, reducer return after dealing with a new state. In other words, the state is responsible for managing reducer (or update). In actual use, the status of our application can be divided into a number of modules, such as the state of a typical social networking sites can be divided into: personal information, friends list, message list and other modules. Theoretically, we can manually update with a reducer to deal with all states, but to do so, we will be a logical reducer function too much, prone to confusion. We can thus processing logic (the reducer) are also divided according to the module, each module is subdivided into individual sub-modules, so that we can very clear logical combination. For us this demand, redux provides combineReducers method, the sub-reducer can be combined into a total of reducer.

The main source of combineReducers logic:

function combineReducers(reducers) {
    //先获取传入reducers对象的所有key
    const reducerKeys = Object.keys(reducers)
    const finalReducers = {} // 最后真正有效的reducer存在这里
    //下面从reducers中筛选出有效的reducer
    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);
    
    //这里assertReducerShape函数做的事情是:
    // 检查finalReducer中的reducer接受一个初始action或一个未知的action时,是否依旧能够返回有效的值。
    let shapeAssertionError
  	try {
    	assertReducerShape(finalReducers)
  	} catch (e) {
    	shapeAssertionError = e
  	}
    
    //返回合并后的reducer
    return function combination(state= {}, action){
  		//这里的逻辑是:
    	//取得每个子reducer对应得state,与action一起作为参数给每个子reducer执行。
    	let hasChanged = false //标志state是否有变化
        let nextState = {}
        for(let i = 0; i < finalReducerKeys.length; i++) {
            //得到本次循环的子reducer
            const key = finalReducerKeys[i]
            const reducer = finalReducers[key]
            //得到该子reducer对应的旧状态
            const previousStateForKey = state[key]
            //调用子reducer得到新状态
            const nextStateForKey = reducer(previousStateForKey, action)
            //存到nextState中(总的状态)
            nextState[key] = nextStateForKey
            //到这里时有一个问题:
            //就是如果子reducer不能处理该action,那么会返回previousStateForKey
            //也就是旧状态,当所有状态都没改变时,我们直接返回之前的state就可以了。
            hasChanged = hasChanged || previousStateForKey !== nextStateForKey
        }
        return hasChanged ? nextState : state
    }
} 

Why do we need middleware
Here Insert Picture Description
adding middleware
Here Insert Picture Description
middleware to achieve redux of
createStore redux of () method of the third parameter enhancer

function createStore(reducer, preloadedState, enhancer) {
    if(enhancer是有效的){  
        return enhancer(createStore)(reducer, preloadedState)
    } 
    
    //...
}

Here we can see, enhancer (enhancer can be called) is a function that accepts a 'regular createStore function' as a parameter, the function returns createStore of a strengthened. The strengthening of the process of doing things, in fact, is the transformation of dispatch, add the middleware. redux provided applyMiddleware () method returns is a enhancer. applyMiddleware, by definition, application middleware, a plurality of intermediate inputs, outputs Enhancer, the role is:
1- transformation function acquired from the middleware
2- all transform functions to compose a transformation function
3- transformation method dispatch

This method of source

function applyMiddleware(...middlewares) {
    // 返回一个函数A,函数A的参数是一个createStore函数。
    // 函数A的返回值是函数B,其实也就是一个加强后的createStore函数,大括号内的是函数B的函数体
    return createStore => (...args) => {
        //用参数传进来的createStore创建一个store
        const store  = createStore(...args)
        //注意,我们在这里需要改造的只是store的dispatch方法
        
        let dispatch = () => {  // 一个临时的dispatch
            					//作用是在dispatch改造完成前调用dispatch只会打印错误信息
            throw new Error(`一些错误信息`)
        } 
        //接下来我们准备将每个中间件与我们的state关联起来(通过传入getState方法),得到改造函数。
        const middlewareAPI = {
            getState: store.getState,
            dispatch: (...args) => dispatch(...args)
        }
        //middlewares是一个中间件函数数组,中间件函数的返回值是一个改造dispatch的函数
        //调用数组中的每个中间件函数,得到所有的改造函数
        const chain = middlewares.map(middleware => middleware(middlewareAPI))
        
        //将这些改造函数compose(翻译:构成,整理成)成一个函数
        //用compose后的函数去改造store的dispatch
        dispatch = compose(...chain)(store.dispatch)
        // compose方法的作用是,例如这样调用:
        // compose(func1,func2,func3)
        // 返回一个函数: (...args) => func1( func2( func3(...args) ) )
        // 即传入的dispatch被func3改造后得到一个新的dispatch,新的dispatch继续被func2改造...
        
        //返回store,用改造后的dispatch方法替换store中的dispatch
        return {
            ...store,
            dispatch
        }
    }
}

Redux look at a simple example of the use of:

import { createStore } from 'redux';

// 创建Redux reducer
/**
 * 这是一个 reducer,形式为 (state, action) => state 的纯函数。
 * 描述了 action 如何把 state 转变成下一个 state。
 *
 * state 的形式取决于你,可以是基本类型、数组、对象,
 * 当 state 变化时需要返回全新的对象,而不是修改传入的参数。

 *
 * 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper)
 * 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。
 */
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT': 
    return state - 1;
  default:
    return state;
  }
}

// 创建 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter);

// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() =>
  console.log(store.getState())
);

// 改变内部 state 惟一方法是 dispatch 一个 action。
// action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1

The above code is a redux of the simplest to use
action

Action payload to store the data transmitted from the application. It is the only source of store data, that is to change the store in the state is required to trigger an action. A plain JavaScript object action nature. Action must be used within a string type field to indicate the type of action to be performed, in addition to the type field, structural action object is entirely up to you. In most case, type is defined as a string will be constant. When applying increasing scale, we recommend using a separate module or to store files action.

import { ADD_TODO, REMOVE_TODO } from '../actionTypes'

//action
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}

Use a separate module or file to define the constant action type is not necessary, or even do not need definitions. For small applications, using string to do action type is more convenient. However, in large applications them explicitly defined as a constant or more advantages than disadvantages.

Action 创建函数

Action function is to create a method of generating action. "Action" and "action to create a function" of these two concepts is easy to mix, pay attention to distinguish the best use. Redux action in the creation function simply returns an action:

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

Doing so will allow action to create functions easier porting and testing.
the reducer
the reducer is based on the modified state action to transform it into the next state, but remember that actions have described the fact that things happen, and the application does not describe how to update the state.

(previousState, action) => newState

Reducer is very important to keep clean. Never do these operations where reducer:
1- modify arguments passed;
2- perform operations with side effects, such as jump API requests and routing;
3- impure function call, such as Date.now () or Math.random ( ).

Tip : reducer is a pure function. It is only used to calculate the next state. It should be completely predictable: repeatedly passed the same input must produce the same output. It should not have side effects do operations such as routing API call or jump. These should take place before the dispatch action.

//reducer
function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}

Do not modify the state. Use Object.assign () Create a copy. Can not be used as such Object.assign (state, {visibilityFilter: action.filter}), because it will change the value of the first parameter. You must first parameter to an empty object. You can open the object of the proposal to expand ES7 support operator, thereby using {... state, visibilityFilter: action.filter} to achieve the same purpose. Returns the old state in case of default. When encountered an unknown action, we must return to the old state.

Split and merge Reducer

function onAction(state = defaultState, action) {
    switch (action.type) {
        case Types.THEME_CHANGE://主题
            return {
                ...state,
                theme: action.theme,
            };
        case Types.SHOW_THEME_VIEW://主题
            return {
                ...state,
                customThemeViewVisible: action.customThemeViewVisible,
            };
        case Types.SORT_LANGUAGE://排序
            return Object.assign({}, state, {
                checkedArray: action.checkedArray,
            });
        case Types.REFRESH_ABOUT://关于
            return Object.assign({}, state, {
                [action.flag]: {
                    ...state[action.flag],
                    projectModels: action.projectModels,
                }
            });
        case Types.ABOUT_SHOW_MORE://关于
            return Object.assign({}, state, {
                me: {
                    ...state.me,
                    [action.menuFlag]: action.menuShow
                }
            });
        default:
            return state;
    }
}

The code looks somewhat lengthy, and the subject, sort, updates appear to be independent of each other, they can not be split into separate function or file it, the answer is yes

//主题 theme.js
export default function onTheme(state = defaultState, action) {
    switch (action.type) {
        case Types.THEME_CHANGE:
            return {
                ...state,
                theme: action.theme,
            };
        case Types.SHOW_THEME_VIEW:
            return {
                ...state,
                customThemeViewVisible: action.customThemeViewVisible,
            };
        default:
            return state;
    }
}

//排序 sort.js
export default function onSort(state = defaultState, action) {
    switch (action.type) {
        case Types.SORT_LANGUAGE:
            return Object.assign({}, state, {
                checkedArray: action.checkedArray,
            });
        default:
            return state;
    }
}

//关于 about.js
export default function onAbout(state = defaultState, action) {
    switch (action.type) {
        case Types.REFRESH_ABOUT:
            return Object.assign({}, state, {
                [action.flag]: {
                    ...state[action.flag],
                    projectModels: action.projectModels,
                }
            });
        case Types.ABOUT_SHOW_MORE:
            return Object.assign({}, state, {
                me: {
                    ...state.me,
                    [action.menuFlag]: action.menuShow
                }
            });
        default:
            return state;
    }
}

The combined reducer

After the above steps we will be a big reducer split into different small reducer, but redux principle is to allow only a root reducer, then we need these few small aggregated into a reducer with reducer in. Here we need to use combineReducers Redux provided (reducers).

import {combineReducers} from 'redux'
import theme from './theme'
import sort from './sort'
import about from './about'

const index = combineReducers({
    theme: theme,
    sort: sort,
    about: about,
})
export default index;

combineReducers () does it to generate a function, the function to call your series reducer, each reducer to filter according to a key part of their data and processing in the state, then the results generated and then all of a function reducer merge into a large object. There is no magic. As with other reducers, all reducers if combineReducers () contains no changes state, then it does not create a new object.

Store

Is a container, Store parameters will store two state (the current state tree, and action) incoming reducer.
1- application state is maintained;
2- provide getState () method to get the state;
3- providing dispatch (action) Method state: we can call store.dispatch (action) in any place, including assembly, the XMLHttpRequest callback, even timer;
to 4- subscribe (listener) listener registration;

In the previous chapter, we use combineReducers () will be merged into a plurality of reducer. Now let's create a Store Redux by the createStore ().

import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)

Guess you like

Origin blog.csdn.net/smlljet/article/details/90769999