如果没有中间件,store.dispatch只能接收一个普通对象作为action。在处理异步action时,我们需要在异步回调或者promise函数then内,async函数await之后dispatch。
dispatch({

type:'before-load'
})
fetch('http://myapi.com/${userId}').then({

response =>dispatch({

type:'load',

payload:response

})
})

dispatch({

type:'before-load'
})
fetch('http://myapi.com/${userId}').then({

response =>dispatch({

type:'load',

payload:response

})
})
这样做确实可以解决问题,特别是在小型项目中,高效,可读性强。 但缺点是需要在组件中写大量的异步逻辑代码,不能将异步过程(例如异步获取数据)与dispatch抽象出来进行复用。而采用类似redux-thunk之类的中间件可以使得dispatch能够接收不仅仅是普通对象作为action。例如:
function load(userId){

return function(dispatch,getState){

dispatch({

type:'before-load'

})

fetch('http://myapi.com/${userId}').then({

response =>dispatch({

type:'load',

payload:response

})

})

}

}
//使用方式
dispatch(load(userId))

function load(userId){

return function(dispatch,getState){

dispatch({

type:'before-load'

})

fetch('http://myapi.com/${userId}').then({

response =>dispatch({

type:'load',

payload:response

})

})

}

}
//使用方式
dispatch(load(userId))
使用中间件可以让你采用自己方便的方式dispatch异步action,下面介绍常见的三种。1. redux-thunk
1. redux-thunk1. redux-thunk
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;


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;

2. redux-promise
2. redux-promise2. redux-promise使用redux-promise可以将action或者action的payload写成promise形式。 源码:
export default function promiseMiddleware({ dispatch }) {
return next => action => {

if (!isFSA(action)) {

return isPromise(action) ? action.then(dispatch) : next(action);

}


return isPromise(action.payload)

? action.payload

.then(result => dispatch({ ...action, payload: result }))

.catch(error => {

dispatch({ ...action, payload: error, error: true });

return Promise.reject(error);

})

: next(action);
};
}


export default function promiseMiddleware({ dispatch }) {
return next => action => {

if (!isFSA(action)) {

return isPromise(action) ? action.then(dispatch) : next(action);

}


return isPromise(action.payload)

? action.payload

.then(result => dispatch({ ...action, payload: result }))

.catch(error => {

dispatch({ ...action, payload: error, error: true });

return Promise.reject(error);

})

: next(action);
};
}

3. Redux-saga
3. Redux-saga3. Redux-sagaredux-saga 是一个用于管理应用程序 Side Effect(副作用,例如异步获取数据,访问浏览器缓存等)的 library,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易3.1 基本使用
3.1 基本使用
import { call, put, takeEvery, takeLatest} from 'redux-saga/effects';

//复杂的异步流程操作
function* fetchUser(action){
try{

const user = yield call(API.fetchUser, action.payload);

yield put({type:"USER_FETCH_SUCCEEDED",user:user})
}catch(e){

yield put({type:"USER_FETCH_FAILED",message:e.message})
}
}

//监听dispatch,调用相应函数进行处理
function* mainSaga(){
yield takeEvery("USER_FETCH_REQUESTED",fetchUser);
}

//在store中注入saga中间件
import {createStore,applyMiddleware} from 'redux';
import createSagaMiddleware from 'redux-saga';

import reducer from './reducers';
import mainSaga from './mainSaga';
const sagaMiddleware = createSagaMiddleware();

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

sagaMiddleware.run(mainSaga)


import { call, put, takeEvery, takeLatest} from 'redux-saga/effects';

//复杂的异步流程操作
function* fetchUser(action){
try{

const user = yield call(API.fetchUser, action.payload);

yield put({type:"USER_FETCH_SUCCEEDED",user:user})
}catch(e){

yield put({type:"USER_FETCH_FAILED",message:e.message})
}
}

//监听dispatch,调用相应函数进行处理
function* mainSaga(){
yield takeEvery("USER_FETCH_REQUESTED",fetchUser);
}

//在store中注入saga中间件
import {createStore,applyMiddleware} from 'redux';
import createSagaMiddleware from 'redux-saga';

import reducer from './reducers';
import mainSaga from './mainSaga';
const sagaMiddleware = createSagaMiddleware();

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

sagaMiddleware.run(mainSaga)

3.2 声明式effects,便于测试
3.2 声明式effects,便于测试为了测试方便,在generator中不立即执行异步调用,而是使用call、apply等effects创建一条描述函数调用的对象,saga中间件确保执行函数调用并在响应被resolve时恢复generator。
function* fetchProducts() {
const products = yield Api.fetch('/products')
dispatch({ type: 'PRODUCTS_RECEIVED', products })
}

//便于测试
function* fetchProducts() {
const products = yield call(Api.fetch, '/products')
//便于测试dispatch
yield put({ type: 'PRODUCTS_RECEIVED', products })
// ...
}
// Effect -> 调用 Api.fetch 函数并传递 `./products` 作为参数
{
CALL: {

fn: Api.fetch,

args: ['./products']
}
}


function* fetchProducts() {
const products = yield Api.fetch('/products')
dispatch({ type: 'PRODUCTS_RECEIVED', products })
}

//便于测试
function* fetchProducts() {
const products = yield call(Api.fetch, '/products')
//便于测试dispatch
yield put({ type: 'PRODUCTS_RECEIVED', products })
// ...
}
// Effect -> 调用 Api.fetch 函数并传递 `./products` 作为参数
{
CALL: {

fn: Api.fetch,

args: ['./products']
}
}

3.3 构建复杂的控制流
3.3 构建复杂的控制流saga可以通过使用effect创建器、effect组合器、saga辅助函数来构建复杂的控制流。effect创建器:

take:阻塞性effect,等待store中匹配的action或channel中的特定消息。

put:非阻塞性effect,用来命令 middleware 向 Store 发起一个 action。

call:阻塞性effect,用来命令 middleware 以参数 args 调用函数 fn。

fork:非阻塞性effect,用来命令 middleware 以 非阻塞调用 的形式执行 fn

select:非阻塞性effect,用来命令 middleware 在当前 Store 的 state 上调用指定的选择器
take:阻塞性effect,等待store中匹配的action或channel中的特定消息。put:非阻塞性effect,用来命令 middleware 向 Store 发起一个 action。call:阻塞性effect,用来命令 middleware 以参数 args 调用函数 fn。fork:非阻塞性effect,用来命令 middleware 以 非阻塞调用 的形式执行 fnselect:非阻塞性effect,用来命令 middleware 在当前 Store 的 state 上调用指定的选择器effect组合器:race:阻塞性effect:用来命令 middleware 在多个 Effect 间运行 竞赛(Race)
function fetchUsersSaga {
const { response, cancel } = yield race({
response: call(fetchUsers),
cancel: take(CANCEL_FETCH)
})
}

function fetchUsersSaga {
const { response, cancel } = yield race({
response: call(fetchUsers),
cancel: take(CANCEL_FETCH)
})
}
all: 当 array 或 object 中有阻塞型 effect 的时候阻塞,用来命令 middleware 并行地运行多个 Effect,并等待它们全部完成
function* mySaga() {
const [customers, products] = yield all([
call(fetchCustomers),
call(fetchProducts)
])
}

function* mySaga() {
const [customers, products] = yield all([
call(fetchCustomers),
call(fetchProducts)
])
}
effect辅助函数:takeEvery:非阻塞性effect,在发起(dispatch)到 Store 并且匹配 pattern 的每一个 action 上派生一个 saga
const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() {
while (true) {
const action = yield take(patternOrChannel)
yield fork(saga, ...args.concat(action))
}

})

const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() {
while (true) {
const action = yield take(patternOrChannel)
yield fork(saga, ...args.concat(action))
}

})
takeLatest:非阻塞性,在发起到 Store 并且匹配 pattern 的每一个 action 上派生一个 saga。并自动取消之前所有已经启动但仍在执行中的 saga 任务。
const takeLatest = (patternOrChannel, saga, ...args) => fork(function*() {

let lastTask

while (true) {

const action = yield take(patternOrChannel)

if (lastTask) {

yield cancel(lastTask) // 如果任务已经结束,cancel 则是空操作

}

lastTask = yield fork(saga, ...args.concat(action))

}
})

const takeLatest = (patternOrChannel, saga, ...args) => fork(function*() {

let lastTask

while (true) {

const action = yield take(patternOrChannel)

if (lastTask) {

yield cancel(lastTask) // 如果任务已经结束,cancel 则是空操作

}

lastTask = yield fork(saga, ...args.concat(action))

}
})
throttle:非阻塞性,在 ms 毫秒内将暂停派生新的任务
const throttle = (ms, pattern, task, ...args) => fork(function*() {

const throttleChannel = yield actionChannel(pattern)



while (true) {

const action = yield take(throttleChannel)

yield fork(task, ...args, action)

yield delay(ms)

}

})
const throttle = (ms, pattern, task, ...args) => fork(function*() {

const throttleChannel = yield actionChannel(pattern)



while (true) {

const action = yield take(throttleChannel)

yield fork(task, ...args, action)

yield delay(ms)

}

})