代碼已經上傳到github中,歡迎star或者forkreact
以前異步處理用的是redux-thunk + redux-actions + redux-promise,可是隨着ES6中Generator的出現,人們發現用Generator處理異步能夠更簡單。而redux-saga就是用Generator來處理異步。
redux-saga文檔並無說本身是處理異步的工具,而是說用來處理邊際效應(side effects),這裏的邊際效應你能夠理解爲程序對外部的操做,好比請求後端,好比操做文件。
redux-saga一樣是一個redux中間件,它的定位就是經過集中控制action,起到一個相似於MVC中控制器的效果。
同時它的語法使得複雜異步操做不會像promise那樣出現不少then的狀況,更容易進行各種測試。github
npm install --save redux-saganpm
put、call、takeEvery、takeLatestredux
put至關於redux的dispatch的做用,而call至關於調用函數segmentfault
export function* delayChangeBtnText() { yield delay(1000); yield put(changeBtnText('123')); yield call(consoleMsg, '完成改變'); }
它提供了相似 redux-thunk 的行爲後端
import { takeEvery } from 'redux-saga' function* watchFetchData() { yield* takeEvery('FETCH_REQUESTED', fetchData) }
takeEvery 容許多個 fetchData 實例同時啓動。在某個特定時刻,儘管以前還有一個或多個 fetchData 還沒有結束,咱們仍是能夠啓動一個新的 fetchData 任務,
若是咱們只想獲得最新那個請求的響應(例如,始終顯示最新版本的數據)。咱們可使用 takeLatest輔助函數api
import { takeLatest } from 'redux-saga' function* watchFetchData() { yield* takeLatest('FETCH_REQUESTED', fetchData) }
import { createStore, applyMiddleware } from 'redux'; // import thunk from 'redux-thunk'; import createSagaMiddleware from 'redux-saga'; import {watchAppSearch} from './sagas'; import rootReducer from './reducers'; /** * saga用法 * 1.建立一個 Saga middleware * 2.使用 applyMiddleware 將 middleware 鏈接至 Store * 3.使用 sagaMiddleware.run(helloSaga) 運行 Saga */ const sagaMiddleware = createSagaMiddleware(); // 建立store的時候,第二個參數是中間件,redux-thunk提供了一個thunk中間件,用於處理異步的action let store = createStore( rootReducer, applyMiddleware(sagaMiddleware) ); // 運行並監控各個action sagaMiddleware.run(watchAppSearch); export default store
import { put, call, takeEvery,takeLatest } from 'redux-saga/effects'; import { actionCreators } from './action' import $api from '../api/index.js'; /** * 處理編輯效應的函數 */ export function* appSearch(action) { // 在saga中這裏經過action.payload獲取到前臺傳過來的keyword內容 const p = function(){ return $api.lookUp({ keyword:action.payload }) .then(res => res.results) .then(res =>{ return res }) } const res = yield call(p); // 執行p函數,返回值賦值給res yield put(actionCreators.saveSearchList(res));// 經過put觸發dispatch ,將返回數據傳過去 } /** * 監控Action的函數 */ // takeLatest 和 takeEvery 不一樣,在任什麼時候刻 takeLatest 只容許一個 fetchData 任務在執行。 // 而且這個任務是最後被啓動的那個。 若是已經有一個任務在執行的時候啓動另外一個 fetchData ,那以前的這個任務會被自動取消。 export function* watchAppSearch() { yield takeEvery(actionCreators.appSearch, appSearch); }
// 同時啓動多個Sagas 監聽action動做 export default function* rootSaga() { yield all([ takeLatest(actionCreators.appSearch, appSearch), takeLatest(actionCreators.getRecommendList, getRecommendList) ]) } /** * app搜索獲取結果列表 */ export function* appSearch(action) { // 在saga中這裏經過action.payload獲取到前臺傳過來的keyword內容 const p = function(){ return $api.lookUp({ keyword:action.payload }) .then(res => res.results) .then(res =>{ return res }) } const res = yield call(p); // 執行p函數,返回值賦值給res yield put(actionCreators.saveSearchList(res));// 經過put觸發dispatch ,將返回數據傳過去 } /** * 請求獲取推薦列表 * @param {*} action */ export function* getRecommendList(action) { const p = function(){ return $api.recommendData({}) .then(res => res.feed) .then(res =>{ return res }) } const res = yield call(p); // 執行p函數,返回值賦值給res yield put(actionCreators.getRecommendListSucceeded(res.entry)); }
redux-actions用來簡化redux重複代碼,這部分簡化工做主要集中在構造action和處理reducers方面。promise
npm install --save redux-actionsapp
import * as types from './action-types' import { createActions } from 'redux-actions'; /** * 使用redux-actions以前 */ // export const saveSearchList = (searchList) => { // console.log('searchList',searchList) // return { // type: types.SAVE_SERACH_LIST, // searchList // } // } // export const removeSearchList = () => { // return { // type: types.REMOVE_SERACH_LIST // } // } /** * 使用redux-actions以後 */ // 使用createAction建立單個動做 // export const saveSearchList = createAction(types.SAVE_SERACH_LIST,searchList=>searchList); // export const removeSearchList = createAction(types.REMOVE_SERACH_LIST); // 使用createActions建立多個動做 export const actionCreators = createActions({ [types.SAVE_SEARCH_LIST]:searchList=>searchList, [types.REMOVE_SEARCH_LIST]:()=>null });
import * as types from './action-types' import { handleActions } from 'redux-actions'; let defaultState = { searchList: []//搜索結果列表 } /** * 使用redux-actions以前 */ // 修改state // export default(state = defaultState, action={})=>{ // switch (action.type) { // case types.SAVE_SERACH_LIST: // return { // ...state, // searchList: action.searchList // } // case types.REMOVE_SERACH_LIST: // return{ // ...state, // searchList:[] // } // default: // return state // } // } /** * 使用redux-actions以後 */ // handleAction單個action處理 // const reducer = handleAction(types.SAVE_SERACH_LIST,(state, action)=>{ // return { // ...state, // searchList: action.searchList // } // },defaultState); // 使用handleActions處理多個actions ,這裏須要注意的是 經過action.payload獲取傳過來的數據 const reducerCreators = handleActions({ [types.SAVE_SEARCH_LIST]:(state, action)=>{ return { ...state, searchList: action.payload } }, [types.REMOVE_SEARCH_LIST]:(state, action)=>{ return{ ...state,st searchList:[] } } },defaultState); export default reducerCreators;
import React, { Component } from 'react'; import { connect } from 'react-redux'; import { actionCreators } from '../../store/action' /** * 使用redux-action以前 */ // const mapDispatchToProps = (dispatch) => ({ // 分發由action creators建立的actions // saveSearchList: searchList => dispatch(saveSearchList(searchList)), // removeSearchList: () => dispatch(removeSearchList()) // }) /** * 使用redux-action以後 */ // createActions會返回一個對象,對象針對每一個action類型都有一個值爲action工廠的屬性,屬性名爲action類型的值去掉下劃線後的駝峯命名 const mapDispatchToProps = { saveSearchList:actionCreators.saveSearchList, removeSearchList:actionCreators.removeSearchList } // 經過connect生成容器組件 export default connect(mapStateToProps,mapDispatchToProps)(SearchResult);
代碼已經上傳到github中,歡迎star或者fork