redux处理异步action解决方案
如果没有中间件,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抽象出来进行复用。而采用类似redux-thunk之类的中间件可以使得dispatch能够接收不仅仅是普通对象作为action。例如:
functionload(userId){
returnfunction(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
functioncreateThunkMiddleware(extraArgument){
return({dispatch,getState})=>next=>action=>{
if(typeofaction==='function'){
returnaction(dispatch,getState,extraArgument);
}
returnnext(action);
};
}
constthunk=createThunkMiddleware();
thunk.withExtraArgument=createThunkMiddleware;
exportdefaultthunk;
2.redux-promise
使用redux-promise可以将action或者action的payload写成promise形式。源码:
exportdefaultfunctionpromiseMiddleware({dispatch}){
returnnext=>action=>{
if(!isFSA(action)){
returnisPromise(action)?action.then(dispatch):next(action);
}
returnisPromise(action.payload)
?action.payload
.then(result=>dispatch({...action,payload:result}))
.catch(error=>{
dispatch({...action,payload:error,error:true});
returnPromise.reject(error);
})
:next(action);
};
}
3.Redux-saga
redux-saga是一个用于管理应用程序SideEffect(副作用,例如异步获取数据,访问浏览器缓存等)的library,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易
3.1基本使用
import{call,put,takeEvery,takeLatest}from'redux-saga/effects';
//复杂的异步流程操作
function*fetchUser(action){
try{
constuser=yieldcall(API.fetchUser,action.payload);
yieldput({type:"USER_FETCH_SUCCEEDED",user:user})
}catch(e){
yieldput({type:"USER_FETCH_FAILED",message:e.message})
}
}
//监听dispatch,调用相应函数进行处理
function*mainSaga(){
yieldtakeEvery("USER_FETCH_REQUESTED",fetchUser);
}
//在store中注入saga中间件
import{createStore,applyMiddleware}from'redux';
importcreateSagaMiddlewarefrom'redux-saga';
importreducerfrom'./reducers';
importmainSagafrom'./mainSaga';
constsagaMiddleware=createSagaMiddleware();
conststore=createStore(reducer,initalState,applyMiddleware(sagaMiddleware));
sagaMiddleware.run(mainSaga)
3.2声明式effects,便于测试
为了测试方便,在generator中不立即执行异步调用,而是使用call、apply等effects创建一条描述函数调用的对象,saga中间件确保执行函数调用并在响应被resolve时恢复generator。
function*fetchProducts(){
constproducts=yieldApi.fetch('/products')
dispatch({type:'PRODUCTS_RECEIVED',products})
}
//便于测试
function*fetchProducts(){
constproducts=yieldcall(Api.fetch,'/products')
//便于测试dispatch
yieldput({type:'PRODUCTS_RECEIVED',products})
//...
}
//Effect->调用Api.fetch函数并传递`./products`作为参数
{
CALL:{
fn:Api.fetch,
args:['./products']
}
}
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上调用指定的选择器
effect组合器:
race:阻塞性effect:用来命令middleware在多个Effect间运行竞赛(Race)
functionfetchUsersSaga{
const{response,cancel}=yieldrace({
response:call(fetchUsers),
cancel:take(CANCEL_FETCH)
})
}
all:当array或object中有阻塞型effect的时候阻塞,用来命令middleware并行地运行多个Effect,并等待它们全部完成
function*mySaga(){
const[customers,products]=yieldall([
call(fetchCustomers),
call(fetchProducts)
])
}
effect辅助函数:
takeEvery:非阻塞性effect,在发起(dispatch)到Store并且匹配pattern的每一个action上派生一个saga
consttakeEvery=(patternOrChannel,saga,...args)=>fork(function*(){
while(true){
constaction=yieldtake(patternOrChannel)
yieldfork(saga,...args.concat(action))
}
})
takeLatest:非阻塞性,在发起到Store并且匹配pattern的每一个action上派生一个saga。并自动取消之前所有已经启动但仍在执行中的saga任务。
consttakeLatest=(patternOrChannel,saga,...args)=>fork(function*(){
letlastTask
while(true){
constaction=yieldtake(patternOrChannel)
if(lastTask){
yieldcancel(lastTask)//如果任务已经结束,cancel则是空操作
}
lastTask=yieldfork(saga,...args.concat(action))
}
})
throttle:非阻塞性,在ms毫秒内将暂停派生新的任务
constthrottle=(ms,pattern,task,...args)=>fork(function*(){
constthrottleChannel=yieldactionChannel(pattern)
while(true){
constaction=yieldtake(throttleChannel)
yieldfork(task,...args,action)
yielddelay(ms)
}
})
到此这篇关于redux处理异步action解决方案的文章就介绍到这了,更多相关redux异步action内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。