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

IT技术 reactjs redux redux-thunk redux-saga
2021-05-26 04:47:50

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

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

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

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

4个回答

但我不能用 saga 做到这一点,因为 saga 不返回Promise

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

以异步用行动履行通常的方式redux-saga是分裂原的行动ACTION_REQUESTACTION_SUCCESSACTION_FAILURE变种。然后 reducer 只接受 SUCCESS/FAILURE 操作,并且可能请求乐观更新

在这种情况下,您的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操作本身与Promise等待无关 - 它只是将异步等待委托给 saga 进程管理器。

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

因此,在成功调用 api 后,您可以执行以下可能的操作。

  1. 进行另一个 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));
  }
}
  1. 成功 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));
     }
    

    }

  2. 更新 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
        }
    }
    

我被同样的问题困住了......

我的解决方案是将调度包装在一个Promise中,并在一个 saga 函数中调用解决和拒绝...

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

我希望这可以帮助某人。

你可以这样做。从组件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)
  ]);
}

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

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



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