Skip to content

Latest commit

 

History

History
238 lines (197 loc) · 8.39 KB

redux.md

File metadata and controls

238 lines (197 loc) · 8.39 KB

Redux

redux 是一个状态state管理器,state和视图是一一对应的。即通过视图我能知道对应的state,通过state也能知道对应的视图。

在react中控制视图刷新的有两个属性:statepropsreduxreact中实现state调整对应视图刷新,是用到reactprops。这里还是需要简单的区别一下。

redux采用发布订阅的方式将stateview连接到一起。redux提供了subscribe函数进行订阅state的调整。一般我们会在根视图做这个监听。

const render = (Component) => {
  ReactDOM.render(
    <AppContainer>
      <Component store={store} />
    </AppContainer>,
    document.getElementById('app'),
  );
};

render(App);
//订阅,state修改重新刷新视图
store.subscribe(() => {
	render(App);
});

Redux API

redux提供了四个API,分别是:

  • createStore
  • combineReducers
  • applyMiddleware
  • bindActionCreators

基础应用最常用的两个API:createStorecombineReducers。我们接下来一一说明一下各个API。

createStore

var store = createStore(reducer)

createStore携带一个reducer参数并返回store的函数。这个函数一般只能调用一次。因为redux要求store全局只能有一个。这里引申出了两个API:storereducer

reducer

reducer = function(state,action){return newState}

reducer其实是一个携带stateaction参数的纯函数。他的作用是根据老的state和新的action返回一个新的state。简单理解就是reducer是负责生产state的函数。

简单reducer函数示例:

export default (state = [] , action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return [
                ...state,
                {
                    id: action.id,
                    text: action.text,
                    completed: action.completed,
                },
            ];
        default:
            return state;
    }
};

store

redux要求store必须只能是一个,可以把它类比成数据库实例。store提供了有以下几个API: dispatchgetState subscribe

  • dispatch(action)

dispatch(action)功能是派发一个action(PS:action后面会详细讲,这里知道他是包含type属性的简单对象,类似:{type:"TODO"})。

  • getState()

getState()是一个无参函数,返回当前的state。这里可以类比一下react中的statereac体重state改变会导致视图刷新,同理getState返回的新值,也会导致页面刷新。redux中的设计思想是:stateview是一一对应的。即知道view能猜到对应的state,知道state我也能猜到对应的view

  • subscribe(func)

subscribe从名字看就知道是订阅的意思,那他订阅什么呢?前面我们说到:dispatch(action) -> reducer -> new state -> getState()这么一个过程。这里面是不是少了点什么?,说好的stateview一一对应,view呢?这就是subscribe的作用了。

subscribe监听所有的dispatch操纵。调用dispatch(action)操作后,所有需要获取新数据的地方(一般在render中),都需要通过getState函数获取。

 /**
   * Adds a change listener. It will be called any time an action is dispatched,
   * and some part of the state tree may potentially have changed. You may then
   * call `getState()` to read the current state tree inside the callback.
   */

subscribe函数惨一个function参数,一般情况下在根组件中处理这个监听。在监听处理函数中刷新根组件。代码示例如下:

import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';

import App from './App';
import store from './store';

const render = (Component) => {
  ReactDOM.render(
    <AppContainer>
      <Component store={store} />
    </AppContainer>,
    document.getElementById('app'),
  );
};

render(App);

// Webpack Hot Module Replacement API
if (module.hot) {
  module.hot.accept('./App', () => { render(App); });
}

store.subscribe(() => {
	render(App);
});

那每次都从根组件渲染,会不会影响性能和组件刷新界面闪烁问题呢?因为react采用的是diff算法,所以性能问题应该不太会有,界面刷新走了也是reactprops改变页面刷新的逻辑,所以理论上不会闪烁。

combineReducers

前面讲到createStore(reducer)传入reducer参数,reducer是一个纯函数返回新的state。代码类似下面:

export default (state = [] , action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return [
                ...state,
                {
                    id: action.id,
                    text: action.text,
                    completed: action.completed,
                },
            ];
        default:
            return state;
    }
};

如果一个复杂的前段引用,这么写reducer会有无数的swicth分支,reducer就会变大巨大和臃肿,这肯定不是我们想要的。所以redux提供了combineReducer函数,从名字上就能看出他的意思:组合reducer。可以看下面代码示例:

import { combineReducers } from 'redux';

import counter from './counter';
import todo from './todo';

export default combineReducers({
    counter, //特别注意这是 class properties写法,还可以写成:"counter":counter。
    todo,
});

当然你还可以在每个独立的reducer中继续拆分。例如在todo reducer中在用combineReducer组合,然后 export 出组合后的的reducer

applyMiddleware

import { createStore } from 'redux';
import reducers from '../reducers';
import thunk from 'redux-thunk'

const store = createStore(
	reducers,
  applyMiddleware(thunk), //支持传多个
);

export default store;

redux提供了中间件支持,用于完善redux复杂应用中的使用。比如我们这里抛出一个Async Action概念(后面讲)。

先解释一下中间件(PS:个人理解):一般就是衔接两个应用(or服务),并提供一些增强的功能。比如常说的:日志中间件、操作统计中间件、请求拦截中间件等。

现在我们知道applyMiddleware的作用,就是负责增强redux功能。那增强redux什么功能?又是怎么增强的呢?看源码把:

export default function applyMiddleware(...middlewares) {
  return (createStore) => (...args) => {
    const store = createStore(...args)
    let dispatch = store.dispatch
    let chain = []

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

分解代码:

  • 所有的中间件传递一个包含:getState和dispatch的简单对象参数
const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
  • 合并所有的中间件,重构成新的dispatch
dispatch = compose(...chain)(store.dispatch)
return {
      ...store,
      dispatch
    }

那这就清楚了,redux的中间件是增强dispatch的。怎么增强呢:多次包装(PS:要清楚一点,dispatch也是一个纯函数)。就是先执行中间件的dispatch函数,最后执行reduxdispatch函数。就是这么简单粗暴的理解它。

bindActionCreators

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

这个函数so easy。正常我们的写法如下:

var action = ActionCreator();
dispatch(action)

这么写其实我觉得挺好,简单易懂。但是redux觉得,这么写一个地方还好,要是十个,百个地方都这么写,就不太好了。所以简化一下写法:

bindActionCreator(ActionCreator,dispatch);

// or 
bindActionCreators({onAdd}, dispatch);

// or
bindActionCreators({onAddd});

总结

淅淅沥沥如雨,昏昏沉沉如梦。我们完整了最基本的梦醒,对redux有了最基本的打开天窗说亮化的了解。如果想要深入浅出的了解他,你应该进入他的体内。别想污了,我说的是源码。