如何在 saga 异步请求后设置状态

Posted

技术标签:

【中文标题】如何在 saga 异步请求后设置状态【英文标题】:how to setstate after saga async request 【发布时间】:2018-05-05 17:41:53 【问题描述】:

我在我的项目中使用 redux-saga。 我以前用过 redux-thunk,所以我不能 setState 结束一些异步请求。喜欢

this.props.thunkAsync()
  .then()
    this.setState( '' );
  

由于 thunk 返回承诺,我可以使用 'then'。 但我不能用 saga 做到这一点,因为 saga 不会返回承诺。 所以我通过检查标志道具(如 REQUEST_SUCCESS、REQUEST_WAITING...)在 componentWillReceiveProps 中进行了更改。 我认为这不是解决这个问题的好方法。

所以.. 我的问题是,当异步请求以 redux-saga 结束时,我该如何做一些工作!

【问题讨论】:

你可以把 react 的 state 设置为 redux state,这样你就可以在 saga 本身中设置 state,并在这个组件中使用 redux state。 我想到了..谢谢!但是如果我想调用一些只能在本地工作的函数呢? 如果你使用 redux 来管理状态,那么你可以触发一个动作并在你的 reducer 中处理它来设置状态。 @godsenal 如果可以的话,您很可能应该将这些功能移至 saga。 @AnandS 谢谢!但是如果我想在异步请求结束后更改内联样式怎么办?我应该将此样式对象(或某些局部变量)移动到我的 redux 状态吗? 【参考方案1】:

但我不能用 saga 做这个,因为 saga 不返回承诺

Redux-sagathunk 略有不同,因为它是流程管理器,而不是简单的中间件:thunk 仅对触发的操作执行反应,但 saga 有自己的“流程”(正式回调刻度域)和可以通过效果来操作。

使用redux-saga 执行异步操作的通常方法是将原始操作拆分为ACTION_REQUESTACTION_SUCCESSACTION_FAILURE 变体。然后 reducer 只接受 SUCCESS/FAILURE 操作,并且可能对optimistic updates 进行请求。

在这种情况下,您的saga 进程可能如下所示

function* actionNameSaga(action) 
    try     
        const info = yield call(fetch,  params: action.params 
        yield put('ACTION_NAME_SUCCESS', info)
     catch(err) 
        yield put('ACTION_NAME_FAILURE', err)
    

function* rootSaga() 
    yield takeEvery('ACTION_NAME', actionNameSaga)

请记住,yield 操作本身与承诺等待无关 - 它只是将异步等待委托给 saga 流程管理器。

【讨论】:

【参考方案2】:

您进行的每个 api 调用都作为异步请求处理,但使用 saga 中的生成器函数进行处理。

所以,在一次成功的 api 调用之后,你可以做以下可能的事情。

    再次调用 api
function* onLogin(action) 
  try 
    const  userName, password  = action;
    const response = yield call(LoginService.login, userName, password);
    yield put(LoginService.loginSuccess(response.user.id));
    const branchDetails = yield call(ProfileService.fetchBranchDetails, response.user.user_type_id);
    yield put(ProfileActions.fetchBranchDetailsSuccess(branchDetails));
   catch (error) 
    yield put(ProfileActions.fetchUserDetailsError(error));
  

    api成功后传回调

    onLoginClick() 
      const  userName, password  = this.state;
      this.props.login(userName, password, this.onLoginSuccess);
    
    
    onLoginSuccess(userDetails) 
      this.setState( userDetails );
    
    
    function *onLogin(action) 
      try 
         const  userName, password, onLoginSuccess  = action;
         const response = yield call(LoginService.login, userName, password);
         if (onLoginSuccess) 
             onLoginSuccess(response);
         
    
         yield put(LoginService.loginSuccess(response.user.id));
         const branchDetails = yield call(ProfileService.fetchBranchDetails, 
         response.user.user_type_id);
         yield put(ProfileActions.fetchBranchDetailsSuccess(branchDetails));
      catch (error) 
         yield put(ProfileActions.fetchUserDetailsError(error));
     
    

    更新 Reducer State 并通过 mapStateToProps 从 props 中获取

    yield put(LoginService.loginSuccess(response.user.id));        
    @connect(
        state => (
            usedDetails: state.user.get('usedDetails'),
        )
    )
    static getDerivedStateFromProps(nextProps, prevState) 
        const  usedDetails  = nextProps;
        return 
            usedDetails
        
    
    

【讨论】:

【参考方案3】:

你可以这样做。从组件 props 调用 saga 方法并传递成功或失败后要执行的函数,如下所示

export function* login( payload ) 
  const url = 'localhost://login.json';

 try 
    const response = yield call(App_Service,  url, method: 'GET' );
 if (response.result === 'ok' && response.data.body) 
    yield put(fetchDataActionCreators.getLoginSuccess(response.data.body));
    //function passed as param from component, pass true if success
    payload.callback(true);
  


  catch (e) 
        //function passed as param from component, pass false if failure
    payload.callback(false);
    console.log(e);
  



export function* watchLogin() 
  while (true) 
    const action = yield take(LOGIN);
    yield* login(action);
  




export default function* () 
  yield all([
    fork(watchLogin)
  ]);

在你作为参数传递给saga的函数中调用setState方法的组件

login() 
    // store
    this.props.getServiceDetails(
       callback:(success) => this.onLoginSuccess(success)
    )
  



onLoginSuccess = (success) => 
    this.setState(
       login:success
    )
    alert('login '+success)
  

【讨论】:

【参考方案4】:

我遇到了同样的问题...

我的解决方案是将调度包装在一个承诺中,并在一个传奇函数中调用解析和拒绝......

我创建了一个挂钩来包装调度。你可以在这里看到我的例子: https://github.com/ricardocanelas/redux-saga-promise-example

希望对大家有所帮助。

【讨论】:

以上是关于如何在 saga 异步请求后设置状态的主要内容,如果未能解决你的问题,请参考以下文章

redux-saga的简单使用——saga的模块化拆分——saga进行网络请求——同步修改状态

redux-saga的简单使用——saga的模块化拆分——saga进行网络请求——同步修改状态

请求完成后触发 Redux 异步操作。为啥?

Redux:从组件请求成功或错误流(使用 redux-saga)

异步方式

使用 redux-saga 对 redux store 进行异步调用