2018-09-29

Redux中间件


redux

2. redux [#](#t12. redux)

2.1 index.js [#](#t22.1 index.js)

import createStore from './createStore';
import combineReducers from './combineReducers';
import bindActionCreators from './bindActionCreators';
import compose from './compose';
import applyMiddleware from './applyMiddleware';
export {
    createStore,
    combineReducers,
    bindActionCreators,
    compose,
    applyMiddleware
}

2.2 createStore.js [#](#t32.2 createStore.js)

export default function (reducer, preloadedState,enhancer) {
    if (typeof enhancer !== 'undefined') {
       return enhancer(createStore)(reducer, preloadedState)
    }
    let state = preloadedState;
    let listeners = [];
    function getState() {
        // return state;
        return JSON.parse(JSON.stringify(state));
    }
    //派发分发的意思
    //action 动作 描述一下你想干什么,动作是一个普通的JS对象,只有一个属性是必须的。type,其它属性随意
    function dispatch(action) {
        //接收新的动作后,通过 才状态 和新动作计算出新状态
        state = reducer(state, action);
        //然后通过所有的监听函数执行
        listeners.forEach(listener => listener());
    }
    //派发了一个动作获取初始值,其实在redux内部是派发一个INIT: '@@redux/INIT'动作
    dispatch({ type: '@@redux/INIT' });
    //订阅,供外界订阅本仓库中状态的变化 ,如果状态 变化 了会执行订阅的逻辑
    function subscribe(listener) {
        listeners.push(listener);
        //返回一个取消订阅函数
        return function () {
            listeners = listeners.filter(item => item != listener)
        }
    }
    return {
        getState, dispatch, subscribe
    }
}

2.3 combineReducers.js [#](#t42.3 combineReducers.js)

export default reducers => (state = {}, action) => Object.keys(reducers).reduce((currentState, key) => {
    currentState[key] = reducers[key](state[key], action);
    return currentState;
}, {});

2.4 bindActionCreators.js [#](#t52.4 bindActionCreators.js)

export default function bindActionCreators(actions,dispatch){
    let newActions = {};
    for(let attr in actions){
     newActions[attr] = function(){
         dispatch(actions[attr].apply(null,arguments));
     }
    }
    return newActions;
 }

2.5 compose.js [#](#t62.5 compose.js)

export default function (...funcs){
    return funcs.reduce((a,b)=>(...args)=>a(b(...args)));
}

2.6 applyMiddleware.js [#](#t72.6 applyMiddleware.js)

import compose from './compose';
export default function(...middlewares){
    return function(createStore){
        return function(reducer){
            let store = createStore(reducer);
            let dispatch;
            middlewares = middlewares.map(middleware=>middleware({
                getState:store.getState,
                dispatch:action=>dispatch(action)
            }));
            dispatch = compose(...middlewares)(store.dispatch);
            return {...store,dispatch};
        }
    }
}

3. react-redux [#](#t83. react-redux)

index.js

import connect from './connect';
import Provider from './Provider';
export {
    connect,
    Provider
}

3.1 connect.js [#](#t93.1 connect.js)

import React,{Component} from 'react';
import {bindActionCreators} from '../redux';
import propTypes from 'prop-types';
export default function(mapStateToProps,mapDispatchToProps){
   return function(WrapedComponent){
      class ProxyComponent extends Component{
          static contextTypes = {
              store:propTypes.object
          }
          constructor(props,context){
            super(props,context);
            this.store = context.store;
            this.state = mapStateToProps(this.store.getState());
          }
          componentWillMount(){
              this.unsubscribe = this.store.subscribe(()=>{
                  this.setState(mapStateToProps(this.store.getState()));
              });
          }
          componentWillUnmount(){
              this.unsubscribe();
          }
          render(){
              let actions= {};
              if(typeof mapDispatchToProps == 'function'){
                actions = mapDispatchToProps(this.store.disaptch);
              }else if(typeof mapDispatchToProps == 'object'){
                actions = bindActionCreators(mapDispatchToProps,this.store.dispatch);
              }
                return <WrapedComponent {...this.state} {...actions}/>
         }
      }
      return ProxyComponent;
   }
}

3.2 Provider.js [#](#t103.2 Provider.js)

//用来通过上下文对象向下层组件传递数据 store
import React,{Component} from 'react';
import propTypes from 'prop-types';
export default class Provider extends Component{
    static childContextTypes = {
        store:propTypes.object.isRequired
    }
    getChildContext(){
        return {store:this.props.store};
    }
    render(){
        return this.props.children;
    }
}

4. 中间件 [#](#t114. 中间件)

4.1 自己实现日志中间件 [#](#t124.1 自己实现日志中间件)

我们改写了,dispatch方法实现了在更改状态时打印前后的状态,但是这种方案并不好。所以我们可以采用中间的方式。

let store = createStore(reducer);
let dispatch = store.dispatch;
store.dispatch = function (action) {
  console.log(store.getState().number);
  dispatch(action);
  console.log(store.getState().number)
};
export default store;

4.2 实现logger中间件 [#](#t134.2 实现logger中间件)

中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能

let logger = store => dispatch => action=>{
  console.log(store.getState().number);
  dispatch(action);
  console.log(store.getState().number)
};
let applyMiddleWare = middleware => createStore => reducer =>{
  let store = createStore(reducer);
  let middle = middleware(store);
  let dispatch = middle(store.dispatch);
  return { //将中间返回的dispatch方法覆盖掉原有store中的dispatch
    ...store,
    dispatch
  }
};
export default applyMiddleWare(logger)(createStore)(reducer);

5.counter #

5.1 Counter.js [#](#t155.1 Counter.js)

import React, { Component } from 'react';
import store from '../store';
import actions from '../store/actions/counter';
import {connect} from '../react-redux';
class Counter extends Component {
    constructor(props){
        super(props);
    }
    render() {
        return (
            <div>
                <p>{this.props.number}</p>
                <button onClick={this.props.increment} >+</button>
                <button onClick={this.props.thunkIncrement} >thunk+</button>
                <button onClick={this.props.promiseIncrement} >promise+</button>
                <button onClick={this.props.payloadIncrement} >payload+</button>
            </div>
        )
    }
}
export default connect(
    state=>state,
    actions
)(Counter);

5.2 actions.js [#](#t165.2 actions.js)

import * as types from '../action-types';
//actionCreator 创建action的函数
export default {
    increment(){
        return {type:types.INCREMENT,payload:1}
    },
    thunkIncrement(){
        return function(dispatch,getState){
            setTimeout(function(){
                dispatch({type:types.INCREMENT,payload:1});
            },1000);
        }
    },
    promiseIncrement(){
        return new Promise(function(resolve){
            setTimeout(function(){
                resolve({type:types.INCREMENT,payload:1});
            },1000);
        });
    },
    payloadIncrement(){
       return {
           type:types.INCREMENT,
           payload:new Promise(function(resolve,reject){
               setTimeout(function(){
                   if(Math.random()>.5){
                       resolve(1);
                   }else{
                       reject(-1);
                   }
               },1000);
           })
       }
    }
}

5.3 reducers.js [#](#t175.3 reducers.js)

import * as types from '../action-types';
export default function(state={number:0},action){
    switch(action.type){
        case types.INCREMENT:
            return {number:state.number + (action.payload)};
        default:
            return state;
    }
}

action-types.js

export const INCREMENT = 'INCREMENT';

5.4 compose.js [#](#t185.4 compose.js)

function add1(str){
    return '1'+str;
}
function add2(str){
    return '2'+str;
}
function add3(str){
    return '3'+str;
}
function compose1(...fns){
  return function(...args){
    let last = fns.pop();
    return fns.reduceRight(function(val,fn){
        return fn(val);
    },last(...args))
  }
}
function compose2(...fns){
    return function(...args){
       return fns.reduce((a,b)=>a(b(...args)));
    }
}

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

let result = compose3(add3,add2,add1)('zxmf');
console.log(result);

5.5 applyMiddleware [#](#t195.5 applyMiddleware)

let applyMiddleware = function(middleware){
    return function(createStore){
        return function(reducer){
            let store = createStore(reducer);
            middleware = middleware(store);
            let dispatch = middleware(store.dispatch);
            return {...store,dispatch}
        }
    }
}

5.6 logger [#](#t205.6 logger)

let logger = function(store){
    return function(next){
        return function(action){
            console.log(store.getState());
            next(action);
            console.log(store.getState());
        }
    }
}

5.7 thunk [#](#t215.7 thunk)

let thunk = function({getState,dispatch}){
    return function(next){
        return function(action){
           if(typeof action == 'function'){
                action(dispatch,getState);
           }else{
               next(action);
           }
        }
    }
}

5.8 promise [#](#t225.8 promise)

let promise = function({getState,dispatch}){
    return function(next){
        return function(action){
           if(action.then){
               action.then(dispatch);
           }else if(action.payload && action.payload.then){
               action.payload.then(payload=>dispatch({...action,payload}),payload=>dispatch({...action,payload}));
           }else{
               next(action);
           }
        }
    }
}

5.9 join.js [#](#t235.9 join.js)

import {createStore} from 'redux';
import reducers from './reducers';
let logger = function(store){
    return function(next){
        return function(action){
            console.log(store.getState());
            next(action);
            console.log(store.getState());
        }
    }
}
let thunk = function({getState,dispatch}){
    return function(next){
        return function(action){
           if(typeof action == 'function'){
                action(dispatch,getState);
           }else{
               next(action);
           }
        }
    }
}
let promise = function({getState,dispatch}){
    return function(next){
        return function(action){
           if(action.then){
               action.then(dispatch);
           }else if(action.payload && action.payload.then){
               action.payload.then(payload=>dispatch({...action,payload}),payload=>dispatch({...action,payload}));
           }else{
               next(action);
           }
        }
    }
}

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

let applyMiddleware = function(...middlewares){
    return function(createStore){
        return function(reducer){
            let store = createStore(reducer);
            let dispatch;
            middlewares = middlewares.map(middleware=>middleware({
                getState:store.getState,
                dispatch:action=>dispatch(action)
            }));
            dispatch = compose(...middlewares)(store.dispatch);
            return {...store,dispatch};
        }
    }
}
let store = applyMiddleware(thunk,promise,logger)(createStore)(reducers);
export default store;

Gitalking ...

Markdown is supported

Be the first guy leaving a comment!