Redux:全揭祕與入坑指北(中)

Cris_冷崢子發表於2019-03-03
  • 什麼是Redux中介軟體
  • 存在中介軟體時倉庫的初始化
  • 建立一箇中介軟體
  • applyMiddleware.js
  • compose.js
  • thunk
  • promise

pre-notify

previously:Redux:全揭祕與入坑指北(上)

上一篇說了下creatStore和react-redux,這一篇主要說一下中介軟體

本來想畫全流程解析圖的,emmm...沒找到好的工具,可以參考下網上流傳的幾張

點我

什麼是Redux中介軟體

Redux:全揭祕與入坑指北(中)
中介軟體是插入在使用者發射action動作之後到reducer接收到這個動作之前這個時機的處理器,它能完成一些額外的邏輯。

存在中介軟體時倉庫的初始化

平時我們是這麼初始化一個有中介軟體的倉庫的

import {createStore,applyMiddleware} from 'redux';

let store = createStore(reducer,{astate:..,bstate:...,...},applyMiddleware(xx,yy,zz));
複製程式碼

其實內部是這樣呼叫的

let store = applyMiddleware(xxx,yyy,...)(createStore)(reducer,{astate:..,bstate:...,...})
複製程式碼

emmm,原理是這樣的

export default function createStore(reducer, preloadedState, enhancer){ // 增強器
  if(enhancer && typeof enhancer === 'function'){
    return enhancer(createStore)(reducer,preloadedState)
  }
  ...
複製程式碼

So,我們現在知道了,如果有中介軟體,會先執行applyMiddleware應用中介軟體這個方法,並且將createStorereducerpreloadedState傳到applyMiddleware這個方法裡面,在裡面初始化倉庫。

建立一箇中介軟體

我們再來看看平時我們是怎麼定義一箇中介軟體的

let logger = ({dispatch,getState})=>next=>action=>{
    //do shome thing else
    console.log(1);
    next(action);
    console.log(11);
    //do some thing else agin
}
複製程式碼

它等價於

let logger = function({dispatch,getState}){
    return function(next){
    	return function(action){
    	    //do shome thing else
            console.log(1);
            next(action);
            console.log(11);
            //do some thing else agin
        }
    }
}
複製程式碼

其中,getState 就是createStore初始化一個倉庫時原本匯出的getState,action也還是使用者所發射的那個reducer所接受的那個action,

dispatch不再是createStore所匯出的那個了,而是經過中介軟體包裝後的dispatch。

另外next其實是另外一箇中介軟體,和Koa中的中介軟體一樣我們需要手動呼叫next來讓佇列中的中介軟體一次執行。並且中介軟體的執行及結果依然遵循洋蔥模型

我們來看這樣一個栗子,假若我們有兩個中介軟體

//中介軟體1
let middle1 = function({dispatch,getState}){
    return function(next){
    	return function(action){
    	    //do shome thing else
            console.log(1);
            next(action);
            console.log(11);
            //do some thing else agin
        }
    }
}

//中介軟體2
let middle2 = function({dispatch,getState}){
    return function(next){
    	return function(action){
    	    //do shome thing else
            console.log(2);
            next(action);
            console.log(22);
            //do some thing else agin
        }
    }
}
複製程式碼

然後我們這樣初始化倉庫時這樣註冊他們

let store = applyMiddleware(middle2,middle1)(createStore)(reducer);
複製程式碼

那麼當我們發射一個動作時,比如說

<button onClick={this.props.increment}>+</button>
複製程式碼

那麼此時就會呼叫派發,只不過此時的派發方法是經過先經過middle1包裝後又經過middle2包裝的方法。它會先執行middler2額外新增的一部分程式碼,然後再執行middle1額外新增的一部分程式碼,然後才會真正的派發,派發完以後又會將middle1剩下的程式碼執行完,最後是middle2剩下的程式碼。

Redux:全揭祕與入坑指北(中)

執行結果會是:先輸出2,再輸出1,再執行store.dispatch,再輸出11,最後輸出22

接下來,我們來看這樣的模型具體是怎樣實現的。

applyMiddleware.js

這個方法最後會將原本的store.dispatch給替換成新的dispatch,這個新的dispatch是則是我們上面所說的經過中介軟體層層包裝後的新的dispatch。

//applyMiddleware.js

export default function(...middlewares){
    return function(createStore){
    	return function(reducer,preloadedState){
    	    let store = createStore(reducer,preloadedState);
            let dispatch;
            let middlewareAPI = {
            	getState:store.getState
                ,dispatch:action=>dispatch(action)
            }
            middlewares = middlewares.map(middleware(middlewareAPI));
            dispatch = compose(...middlewares)(store.dispatch);
            return {...store,dispatch};
        }
    }
}
複製程式碼

compose.js

applyMiddleware.js中我們用到了一個compose方法,

這個方法會把中介軟體序列起來,並且一層包一層,按照applyMiddleware()呼叫時中介軟體註冊的順序,先註冊的會成為洋蔥模型最外面的一層,後註冊的則往裡一層,最裡面是原本的store.dispatch。

//compose.js

export default function(...fns){
    return function(...args){
    	let last = fns.pop();
        return fns.reduceRight((val,fn)=>{
    	    return fn(val);
        },last(...args));
    }
}

//--- --- ---

//高逼格版
export default function(...fns){
    if(fns.length === 1)return fns[0];
    return fns.reduce((a,b)=>(...args)=>a(b(...args)));
}
複製程式碼

為了便於理解compose函式的作用,請看一下以下的例子

function add1(str){
    return str+'com';
}
function add2(str){
    return str+'po';
}
function add3(str){
    return str+'se!';
}

add3(add2(add1('hello,'))); //輸出:hello,compose!

//以上等價於
let add = compose(add3,add2,add1);
add('hello,');
複製程式碼

So,上面的add其實就是我們的中介軟體

Redux:全揭祕與入坑指北(中)

thunk

...
,thunkIncrement(){ 
    return function(dispatch,getState){
      setTimeout(function(){
        dispatch({type:types.INCREMENT,payload:1})
      },1000);
    }
}
...
複製程式碼
//處理自定義非同步操作的中介軟體

let thunk = ({dispatch,getState})=>next=>action=>{ //處理函式的
  if(typeof action == 'function'){
    action(dispatch,getState);  //說明是一個非同步動作,將dispatch等傳給這個非同步動作的函式,在非同步有結果後再執行派發改變原本狀態。
  }else{
    next(action); //匹配下一個中介軟體
  }
};
複製程式碼

promise

...
//1)
,promiseIncrement(){
    return new Promise(function(resolve,reject){
     setTimeout(function(){
       resolve({type:types.INCREMENT,payload:1}); //reject無法處理
     },1000)
    });
}
//2)
,payloadIncrement(){
    return {
      type:types.INCREMENT
      ,payload:new Promise(function(resolve,reject){
        setTimeout(function(){
          if(Math.random()>.5){
            resolve(100);
          }else{
            reject(-100);
          }
        },1000)
      })
    }
}
...
複製程式碼
 //處理promise的中介軟體
 
let promise = ({dispatch,getState})=>next=>action=>{
  if(action.then&&typeof action.then === 'function'){ //是一個promise
    action.then(dispatch);
  }else if(action.payload&&action.payload.then){
    action.payload.then(payload=>dispatch({...action,payload}),payload=>dispatch({...action,payload}));
  } else{
    next(action);
  }
};
複製程式碼

相關文章