2页屠、Redux與它的中間件:redux-thunk,redux-actions蓖柔,redux-promise辰企,redux-saga

序言

這里要講的就是一個(gè)Redux在React中的應(yīng)用問(wèn)題,講一講Redux况鸣,react-redux牢贸,redux-thunk,redux-actions镐捧,redux-promise潜索,redux-saga這些包的作用和他們解決的問(wèn)題。
因?yàn)椴幌氚哑锰L(zhǎng)懂酱,所以沒(méi)有太多源碼分析和語(yǔ)法講解竹习,能怎么簡(jiǎn)單就怎么簡(jiǎn)單。

Redux

先看看百度百科上面Redux的一張圖:

這是Redux在Github上的介紹:Redux用于js程序列牺,是一個(gè)可預(yù)測(cè)的狀態(tài)容器整陌。

在這里我們首先要明白的是什么叫可預(yù)測(cè)?什么叫狀態(tài)容器瞎领?

什么叫狀態(tài)泌辫?實(shí)際上就是變量,對(duì)話框顯示或隱藏的變量九默,一杯奶茶多少錢(qián)的變量震放。

那么這個(gè)狀態(tài)容器,實(shí)際上就是一個(gè)存放這些變量的變量驼修。

你創(chuàng)建了一個(gè)全局變量叫Store殿遂,然后將代碼中控制各個(gè)狀態(tài)的變量存放在里面诈铛,那么現(xiàn)在Store就叫做狀態(tài)容器。

什么叫可預(yù)測(cè)墨礁?

你在操作這個(gè)Store的時(shí)候癌瘾,總是用Store.price的方式來(lái)設(shè)置值,這種操作數(shù)據(jù)的方式很原始饵溅,對(duì)于復(fù)雜的系統(tǒng)而言永遠(yuǎn)都不知道程序在運(yùn)行的過(guò)程中發(fā)生了什么妨退。

那么現(xiàn)在我們都通過(guò)發(fā)送一個(gè)Action去做修改,而Store在接收到Action后會(huì)使用Reducer對(duì)Action傳遞的數(shù)據(jù)做處理蜕企,最后應(yīng)用到Store中咬荷。

相對(duì)于Store.price的方式來(lái)修改者,這種方式無(wú)疑更麻煩轻掩,但是這種方式的好處就是幸乒,每一個(gè)Action里面都可以寫(xiě)日志,可以記錄各種狀態(tài)的變動(dòng)唇牧,這就是可預(yù)測(cè)罕扎。

所以如果你的程序很簡(jiǎn)單,你完全沒(méi)有必要去用Redux丐重。

看看Redux的示例代碼:

actionTypes.js:

export const CHANGE_BTN_TEXT = 'CHANGE_BTN_TEXT';

actions.js:

import * as T from './actionTypes';

export const changeBtnText = (text) => {
  return {
    type: T.CHANGE_BTN_TEXT,
    payload: text
  };
};

reducers.js:

import * as T from './actionTypes';

const initialState = {
  btnText: '我是按鈕',
};

const pageMainReducer = (state = initialState, action) => {
  switch (action.type) {
    case T.CHANGE_BTN_TEXT:
      return {
        ...state,
        btnText: action.payload
      };
    default:
      return state;
  }
};

export default pageMainReducer;

index.js

import { createStore } from 'redux';
import reducer from './reducers';
import { changeBtnText } from './actions';

const store = createStore(reducer);
// 開(kāi)始監(jiān)聽(tīng)腔召,每次state更新,那么就會(huì)打印出當(dāng)前狀態(tài)
const unsubscribe = store.subscribe(() => {
  console.info(store.getState());
});
// 發(fā)送消息
store.dispatch(changeBtnText('點(diǎn)擊了按鈕'));
// 停止監(jiān)聽(tīng)state的更新
unsubscribe();

這里就不解釋什么語(yǔ)法作用了扮惦,網(wǎng)上這樣的資料太多了臀蛛。

Redux與React的結(jié)合:react-redux

Redux是一個(gè)可預(yù)測(cè)的狀態(tài)容器,跟React這種構(gòu)建UI的庫(kù)是兩個(gè)相互獨(dú)立的東西崖蜜。

Redux要應(yīng)用到React中浊仆,很明顯action,reducer豫领,dispatch這幾個(gè)階段并不需要改變抡柿,唯一需要考慮的是redux中的狀態(tài)需要如何傳遞給react組件。

很簡(jiǎn)單等恐,只需要每次要更新數(shù)據(jù)時(shí)運(yùn)用store.getState獲取到當(dāng)前狀態(tài)洲劣,并將這些數(shù)據(jù)傳遞給組件即可。

那么問(wèn)題來(lái)了鼠锈,如何讓每個(gè)組件都獲取到store呢闪檬?

當(dāng)然是將store作為一個(gè)值傳遞給根組件,然后store就會(huì)一級(jí)一級(jí)往下傳购笆,使得每個(gè)組件都能獲取到store的值。

但是這樣太繁瑣了虚循,難道每個(gè)組件需要寫(xiě)一個(gè)傳遞store的邏輯同欠?為了解決這個(gè)問(wèn)題样傍,那么得用到React的context玩法,通過(guò)在根組件上將store放在根組件的context中铺遂,然后在子組件中通過(guò)context獲取到store。

react-redux的主要思路也是如此,通過(guò)嵌套組件Provider將store放到context中毙芜,通過(guò)connect這個(gè)高階組件娄柳,來(lái)隱藏取store的操作,這樣我們就不需要每次去操作context寫(xiě)一大堆代碼那么麻煩了粮坞。

然后我們?cè)賮?lái)基于之前的Redux示例代碼給出react-redux的使用演示代碼蚊荣,其中action和reduce部分不變,先增加一個(gè)組件PageMain:

const PageMain = (props) => {
  return (
    <div>
      <button onClick={() => {
        props.changeText('按鈕被點(diǎn)擊了');
      }}
      >
        {props.btnText}
      </button>
    </div>
  );
};
// 映射store.getState()的數(shù)據(jù)到PageMain
const mapStateToProps = (state) => {
  return {
    btnText: state.pageMain.btnText,
  };
};
// 映射使用了store.dispatch的函數(shù)到PageMain
const mapDispatchToProps = (dispatch) => {
  return {
    changeText: (text) => {
      dispatch(changeBtnText(text));
    }
  };
};

// 這個(gè)地方也可以簡(jiǎn)寫(xiě)莫杈,react-redux會(huì)自動(dòng)做處理
const mapDispatchToProps = {
  changeText: changeBtnText
};

export default connect(mapStateToProps, mapDispatchToProps)(PageMain);

注意上面的state.pageMain.btnText互例,這個(gè)pageMain是我用redux的combineReducers將多個(gè)reducer合并后給的原先的reducer一個(gè)命名。

它的代碼如下:

import { combineReducers } from 'redux';
import pageMain from './components/pageMain/reducers';

const reducer = combineReducers({
  pageMain
});

export default reducer;

然后修改index.js:

import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import reducer from './reducers';
import PageMain from './components/pageMain';

const store = createStore(reducer);

const App = () => (
  <Provider store={store}>
    <PageMain />
  </Provider>
);

ReactDOM.render(<App />, document.getElementById('app'));

Redux的中間件

之前我們講到Redux是個(gè)可預(yù)測(cè)的狀態(tài)容器筝闹,這個(gè)可預(yù)測(cè)在于對(duì)數(shù)據(jù)的每一次修改都可以進(jìn)行相應(yīng)的處理和記錄媳叨。

假如現(xiàn)在我們需要在每次修改數(shù)據(jù)時(shí),記錄修改的內(nèi)容关顷,我們可以在每一個(gè)dispatch前面加上一個(gè)console.info記錄修改的內(nèi)容糊秆。

但是這樣太繁瑣了,所以我們可以直接修改store.dispatch:

let next = store.dispatch
store.dispatch = (action)=> {
  console.info('修改內(nèi)容為:', action)
  next(action)
}

Redux中也有同樣的功能议双,那就是applyMiddleware扩然。直譯過(guò)來(lái)就是“應(yīng)用中間件”,它的作用就是改造dispatch函數(shù)聋伦,跟上面的玩法基本雷同夫偶。

來(lái)一段演示代碼:

import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers';

const store = createStore(reducer, applyMiddleware(curStore => next => action => {
  console.info(curStore.getState(), action);
  return next(action);
}));

看起來(lái)挺奇怪的玩法,但是理解起來(lái)并不難觉增。通過(guò)這種返回函數(shù)的方法兵拢,使得applyMiddleware內(nèi)部以及我們使用時(shí)可以處理store和action,并且這里next的應(yīng)用就是為了使用多個(gè)中間件而存在的逾礁。

而通常我們沒(méi)有必要自己寫(xiě)中間件说铃,比如日志的記錄就已經(jīng)有了成熟的中間件:redux-logger,這里給一個(gè)簡(jiǎn)單的例子:

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
import reducer from './reducers';

const logger = createLogger();

const store = createStore(
  reducer,
  applyMiddleware(logger)
);

這樣就可以記錄所有action及其發(fā)送前后的state的日志嘹履,我們可以了解到代碼實(shí)際運(yùn)行時(shí)到底發(fā)生了什么腻扇。

redux-thunk:處理異步action

在上面的代碼中,我們點(diǎn)擊按鈕后砾嫉,直接修改了按鈕的文本幼苛,這個(gè)文本是個(gè)固定的值。

actions.js:

import * as T from './actionTypes';

export const changeBtnText = (text) => {
  return {
    type: T.CHANGE_BTN_TEXT,
    payload: text
  };
};

但是在我們實(shí)際生產(chǎn)的過(guò)程中焕刮,很多情況都是需要去請(qǐng)求服務(wù)端拿到數(shù)據(jù)再修改的舶沿,這個(gè)過(guò)程是一個(gè)異步的過(guò)程墙杯。又或者需要setTimeout去做一些事情。

我們可以去修改這一部分如下:

const mapDispatchToProps = (dispatch) => {
  return {
    changeText: (text) => {
      dispatch(changeBtnText('正在加載中'));
      axios.get('http://test.com').then(() => {
        dispatch(changeBtnText('加載完畢'));
      }).catch(() => {
        dispatch(changeBtnText('加載有誤'));
      });
    }
  };
};

實(shí)際上括荡,我們每天不知道要處理多少這樣的代碼高镐。

但是問(wèn)題來(lái)了,異步操作相比同步操作多了一個(gè)很多確定因素畸冲,比如我們展示正在加載中時(shí)嫉髓,可能要先要做異步操作A,而請(qǐng)求后臺(tái)的過(guò)程卻非骋叵校快算行,導(dǎo)致加載完畢先出現(xiàn),而這時(shí)候操作A才做完监憎,然后再展示加載中纱意。

所以上面的這個(gè)玩法并不能滿(mǎn)足這種情況。

這個(gè)時(shí)候我們需要去通過(guò)store.getState獲取當(dāng)前狀態(tài)鲸阔,從而判斷到底是展示正在加載中還是展示加載完畢偷霉。

這個(gè)過(guò)程就不能放在mapDispatchToProps中了,而需要放在中間件中褐筛,因?yàn)橹虚g件中可以拿到store类少。

首先創(chuàng)造store的時(shí)候需要應(yīng)用react-thunk,也就是

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

const store = createStore(
  reducer,
  applyMiddleware(thunk)
);

它的源碼超級(jí)簡(jiǎn)單:

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

從這個(gè)里面可以看出渔扎,它就是加強(qiáng)了dispatch的功能硫狞,在dispatch一個(gè)action之前,去判斷action是否是一個(gè)函數(shù)晃痴,如果是函數(shù)残吩,那么就執(zhí)行這個(gè)函數(shù)。

那么我們使用起來(lái)就很簡(jiǎn)單了倘核,此時(shí)我們修改actions.js

import axios from 'axios';
import * as T from './actionTypes';

export const changeBtnText = (text) => {
  return {
    type: T.CHANGE_BTN_TEXT,
    payload: text
  };
};

export const changeBtnTextAsync = (text) => {
  return (dispatch, getState) => {
    if (!getState().isLoading) {
      dispatch(changeBtnText('正在加載中'));
    }
    axios.get(`http://test.com/${text}`).then(() => {
      if (getState().isLoading) {
        dispatch(changeBtnText('加載完畢'));
      }
    }).catch(() => {
      dispatch(changeBtnText('加載有誤'));
    });
  };
};

而原來(lái)mapDispatchToProps中的玩法和同步action的玩法是一樣的:

const mapDispatchToProps = (dispatch) => {
  return {
    changeText: (text) => {
      dispatch(changeBtnTextAsync(text));
    }
  };
};

通過(guò)redux-thunk我們可以簡(jiǎn)單地進(jìn)行異步操作泣侮,并且可以獲取到各個(gè)異步操作時(shí)期狀態(tài)的值。

redux-actions:簡(jiǎn)化redux的使用

Redux雖然好用紧唱,但是里面還是有些重復(fù)代碼活尊,所以有了redux-actions來(lái)簡(jiǎn)化那些重復(fù)代碼。

這部分簡(jiǎn)化工作主要集中在構(gòu)造action和處理reducers方面漏益。

先來(lái)看看原先的actions

import axios from 'axios';
import * as T from './actionTypes';

export const changeBtnText = (text) => {
  return {
    type: T.CHANGE_BTN_TEXT,
    payload: text
  };
};

export const changeBtnTextAsync = () => {
  return (dispatch, getState) => {
    if (!getState().isLoading) {
      dispatch(changeBtnText('正在加載中'));
    }
    axios.get('http://test.com').then(() => {
      if (getState().isLoading) {
        dispatch(changeBtnText('加載完畢'));
      }
    }).catch(() => {
      dispatch(changeBtnText('加載有誤'));
    });
  };
};

然后再來(lái)看看修改后的:

import axios from 'axios';
import * as T from './actionTypes';
import { createAction } from 'redux-actions';

export const changeBtnText = createAction(T.CHANGE_BTN_TEXT, text => text);

export const changeBtnTextAsync = () => {
  return (dispatch, getState) => {
    if (!getState().isLoading) {
      dispatch(changeBtnText('正在加載中'));
    }
    axios.get('http://test.com').then(() => {
      if (getState().isLoading) {
        dispatch(changeBtnText('加載完畢'));
      }
    }).catch(() => {
      dispatch(changeBtnText('加載有誤'));
    });
  };
};

這一塊代碼替換上面的部分代碼后蛹锰,程序運(yùn)行結(jié)果依然保持不變,也就是說(shuō)createAction只是對(duì)上面的代碼進(jìn)行了簡(jiǎn)單的封裝而已绰疤。

這里注意到铜犬,異步的action就不要用createAction,因?yàn)檫@個(gè)createAction返回的是一個(gè)對(duì)象,而不是一個(gè)函數(shù)翎苫,就會(huì)導(dǎo)致redux-thunk的代碼沒(méi)有起到作用权埠。

這里也可以使用createActions這個(gè)函數(shù)同時(shí)創(chuàng)建多個(gè)action榨了,但是講道理煎谍,這個(gè)語(yǔ)法很奇怪,用createAction就好龙屉。

同樣redux-actions對(duì)reducer的部分也進(jìn)行了處理呐粘,比如handleAction以及handelActions。

先來(lái)看看原先的reducers

import * as T from './actionTypes';

const initialState = {
  btnText: '我是按鈕',
};

const pageMainReducer = (state = initialState, action) => {
  switch (action.type) {
    case T.CHANGE_BTN_TEXT:
      return {
        ...state,
        btnText: action.payload
      };
    default:
      return state;
  }
};

export default pageMainReducer;

然后使用handleActions來(lái)處理

import { handleActions } from 'redux-actions';
import * as T from './actionTypes';

const initialState = {
  btnText: '我是按鈕',
};

const pageMainReducer = handleActions({
  [T.CHANGE_BTN_TEXT]: {
    next(state, action) {
      return {
        ...state,
        btnText: action.payload,
      };
    },
    throw(state) {
      return state;
    },
  },
}, initialState);

export default pageMainReducer;

這里handleActions可以加入異常處理转捕,并且?guī)椭幚砹顺跏贾怠?/p>

注意作岖,無(wú)論是createAction還是handleAction都只是對(duì)代碼做了一點(diǎn)簡(jiǎn)單的封裝,兩者可以單獨(dú)使用五芝,并不是說(shuō)使用了createAction就必須要用handleAction痘儡。

redux-promise:redux-actions的好基友,輕松創(chuàng)建和處理異步action

還記得上面在使用redux-actions的createAction時(shí)枢步,我們對(duì)異步的action無(wú)法處理沉删。

因?yàn)槲覀兪褂胏reateAction后返回的是一個(gè)對(duì)象,而不是一個(gè)函數(shù)醉途,就會(huì)導(dǎo)致redux-thunk的代碼沒(méi)有起到作用矾瑰。

而現(xiàn)在我們將使用redux-promise來(lái)處理這類(lèi)情況。

可以看看之前我們使用 createAction的例子:

export const changeBtnText = createAction(T.CHANGE_BTN_TEXT, text => text);

現(xiàn)在我們先加入redux-promise中間件:

import thunk from 'redux-thunk';
import createLogger from 'redux-logger';
import promiseMiddleware from 'redux-promise';
import reducer from './reducers';

const store = createStore(reducer, applyMiddleware(thunk, createLogger, promiseMiddleware));

然后再處理異步action:

export const changeBtnTextAsync = createAction(T.CHANGE_BTN_TEXT_ASYNC, (text) => {
  return axios.get(`http://test.com/${text}`);
});

可以看到我們這里返回的是一個(gè)Promise對(duì)象.(axios的get方法結(jié)果就是Promise對(duì)象)

我們還記得redux-thunk中間件隘擎,它會(huì)去判斷action是否是一個(gè)函數(shù)殴穴,如果是就執(zhí)行。

而我們這里的redux-promise中間件货葬,他會(huì)在dispatch時(shí)采幌,判斷如果action不是類(lèi)似

{
  type:'',
  payload: ''
}

這樣的結(jié)構(gòu),也就是 FSA,那么就去判斷是否為promise對(duì)象震桶,如果是就執(zhí)行action.then的玩法休傍。

很明顯,我們createAction后的結(jié)果是FSA尼夺,所以會(huì)走下面這個(gè)分支尊残,它會(huì)去判斷action.payload是否為promise對(duì)象,是的話那就

action.payload
  .then(result => dispatch({ ...action, payload: result }))
  .catch(error => {
    dispatch({ ...action, payload: error, error: true });
    return Promise.reject(error);
  })

也就是說(shuō)我們的代碼最后會(huì)轉(zhuǎn)變?yōu)椋?/p>

axios.get(`http://test.com/${text}`)
  .then(result => dispatch({ ...action, payload: result }))
  .catch(error => {
    dispatch({ ...action, payload: error, error: true });
    return Promise.reject(error);
  })

這個(gè)中間件的代碼也很簡(jiǎn)單淤堵,總共19行寝衫,大家可以在github上直接看看。

redux-saga:控制器與更優(yōu)雅的異步處理

我們的異步處理用的是redux-thunk + redux-actions + redux-promise拐邪,其實(shí)用起來(lái)還是蠻好用的慰毅。

但是隨著ES6中Generator的出現(xiàn),人們發(fā)現(xiàn)用Generator處理異步可以更簡(jiǎn)單扎阶。

而redux-saga就是用Generator來(lái)處理異步汹胃。

以下講的知識(shí)是基于Generator的婶芭,如果您對(duì)這個(gè)不甚了解,可以簡(jiǎn)單了解一下相關(guān)知識(shí)着饥,大概需要2分鐘時(shí)間犀农,并不難。

redux-saga文檔并沒(méi)有說(shuō)自己是處理異步的工具宰掉,而是說(shuō)用來(lái)處理邊際效應(yīng)(side effects)呵哨,這里的邊際效應(yīng)你可以理解為程序?qū)ν獠康牟僮鳎热缯?qǐng)求后端轨奄,比如操作文件孟害。

redux-saga同樣是一個(gè)redux中間件,它的定位就是通過(guò)集中控制action挪拟,起到一個(gè)類(lèi)似于MVC中控制器的效果挨务。

同時(shí)它的語(yǔ)法使得復(fù)雜異步操作不會(huì)像promise那樣出現(xiàn)很多then的情況,更容易進(jìn)行各類(lèi)測(cè)試玉组。

這個(gè)東西有它的好處谎柄,同樣也有它不好的地方,那就是比較復(fù)雜球切,有一定的學(xué)習(xí)成本谷誓。

并且我個(gè)人而言很不習(xí)慣Generator的用法,覺(jué)得Promise或者await更好用吨凑。

這里還是記錄一下用法捍歪,畢竟有很多框架都用到了這個(gè)。

應(yīng)用這個(gè)中間件和我們的其他中間件沒(méi)有區(qū)別:

import React from 'react';
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import createSagaMiddleware from 'redux-saga';
import {watchDelayChangeBtnText} from './sagas';
import reducer from './reducers';

const sagaMiddleware = createSagaMiddleware();

const store = createStore(reducer, applyMiddleware(promiseMiddleware, sagaMiddleware));

sagaMiddleware.run(watchDelayChangeBtnText);

創(chuàng)建sage中間件后鸵钝,然后再將其中間件接入到store中,最后需要用中間件運(yùn)行sages.js返回的Generator,監(jiān)控各個(gè)action糙臼。

現(xiàn)在我們給出sages.js的代碼:

import { delay } from 'redux-saga';
import { put, call, takeEvery } from 'redux-saga/effects';
import * as T from './components/pageMain/actionTypes';
import { changeBtnText } from './components/pageMain/actions';

const consoleMsg = (msg) => {
  console.info(msg);
};
/**
 * 處理編輯效應(yīng)的函數(shù)
 */
export function* delayChangeBtnText() {
  yield delay(1000);
  yield put(changeBtnText('123'));
  yield call(consoleMsg, '完成改變');
}
/**
 * 監(jiān)控Action的函數(shù)
 */
export function* watchDelayChangeBtnText() {
  yield takeEvery(T.WATCH_CHANGE_BTN_TEXT, delayChangeBtnText);
}

在redux-saga中有一類(lèi)用來(lái)處理邊際效應(yīng)的函數(shù)比如put、call恩商,它們的作用是為了簡(jiǎn)化操作变逃。

比如put相當(dāng)于redux的dispatch的作用,而call相當(dāng)于調(diào)用函數(shù)怠堪。(可以參考上面代碼中的例子)

還有另一類(lèi)函數(shù)就是類(lèi)似于takeEvery揽乱,它的作用就是和普通redux中間件一樣攔截到action后作出相應(yīng)處理。

比如上面的代碼就是攔截到T.WATCH_CHANGE_BTN_TEXT這個(gè)類(lèi)型的action粟矿,然后調(diào)用delayChangeBtnText凰棉。

然后可以回看我們之前的代碼,有這么一行代碼:

sagaMiddleware.run(watchDelayChangeBtnText);

這里實(shí)際就是引入監(jiān)控的這個(gè)生成器后陌粹,再運(yùn)行監(jiān)控生成器撒犀。

這樣我們?cè)诖a里面dispatch類(lèi)型為T(mén).WATCH_CHANGE_BTN_TEXT的action時(shí)就會(huì)被攔截然后做出相應(yīng)處理。

當(dāng)然這里有人可能會(huì)提出疑問(wèn),難道每一個(gè)異步都要這么寫(xiě)嗎或舞,那豈不是要run很多次荆姆?

當(dāng)然不是這個(gè)樣子,我們可以在sage中這么寫(xiě):

export default function* rootSaga() {
  yield [
    watchDelayChangeBtnText()映凳,
    watchOtherAction()
  ]
}

我們只需要按照這個(gè)格式去寫(xiě)胆筒,將watchDelayChangeBtnText這樣用于監(jiān)控action的生成器放在上面那個(gè)代碼的數(shù)組中,然后作為一個(gè)生成器返回魏宽。

現(xiàn)在只需要引用這個(gè)rootSaga即可腐泻,然后run這個(gè)rootSaga决乎。

以后如果要監(jiān)控更多的action队询,只需要在sages.js中加上新的監(jiān)控的生成器即可。

通過(guò)這樣的處理构诚,我們就將sages.js做成了一個(gè)像MVC中的控制器的東西蚌斩,可以用來(lái)處理各種各樣的action,處理復(fù)雜的異步操作和邊際效應(yīng)范嘱。

但是這里要注意,一定要加以區(qū)分sages.js中使用監(jiān)控的action和真正功能用的action送膳,比如加個(gè)watch關(guān)鍵字,以免業(yè)務(wù)復(fù)雜后代碼混亂丑蛤。

總結(jié)

總的來(lái)說(shuō):

  • redux是一個(gè)可預(yù)測(cè)的狀態(tài)容器叠聋,
  • react-redux是將store和react結(jié)合起來(lái),使得數(shù)據(jù)展示和修改對(duì)于react項(xiàng)目而言更簡(jiǎn)單
  • redux中間件就是在dispatch action前對(duì)action做一些處理
  • redux-thunk用于對(duì)異步做操作
  • redux-actions用于簡(jiǎn)化redux操作
  • redux-promise可以配合redux-actions用來(lái)處理Promise對(duì)象受裹,使得異步操作更簡(jiǎn)單
  • redux-saga可以起到一個(gè)控制器的作用碌补,集中處理邊際效用,并使得異步操作的寫(xiě)法更優(yōu)雅棉饶。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末厦章,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子照藻,更是在濱河造成了極大的恐慌袜啃,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幸缕,死亡現(xiàn)場(chǎng)離奇詭異群发,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)发乔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)熟妓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人列疗,你說(shuō)我怎么就攤上這事滑蚯。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵告材,是天一觀的道長(zhǎng)坤次。 經(jīng)常有香客問(wèn)我,道長(zhǎng)斥赋,這世上最難降的妖魔是什么缰猴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮疤剑,結(jié)果婚禮上滑绒,老公的妹妹穿的比我還像新娘。我一直安慰自己隘膘,他們只是感情好疑故,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著弯菊,像睡著了一般纵势。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上管钳,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天钦铁,我揣著相機(jī)與錄音,去河邊找鬼才漆。 笑死牛曹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的醇滥。 我是一名探鬼主播黎比,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼腺办!你這毒婦竟也來(lái)了焰手?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤怀喉,失蹤者是張志新(化名)和其女友劉穎书妻,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體躬拢,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躲履,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了聊闯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片工猜。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖菱蔬,靈堂內(nèi)的尸體忽然破棺而出篷帅,到底是詐尸還是另有隱情史侣,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布魏身,位于F島的核電站惊橱,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏箭昵。R本人自食惡果不足惜税朴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望家制。 院中可真熱鬧正林,春花似錦、人聲如沸颤殴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诅病。三九已至哪亿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贤笆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工讨阻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芥永,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓钝吮,卻偏偏與公主長(zhǎng)得像埋涧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奇瘦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容