如何在 Redux-Saga 中实现回调?

How to achieve callbacks in Redux-Saga?

场景是,我想在调度操作后根据成功、错误回调重定向用户或显示警报。

下面是使用 redux-thunk 完成任务的代码

this.props.actions.login(credentials)
.then((success)=>redirectToHomePage)
.catch((error)=>alertError);

因为 redux-thunk 中的 dispatch 动作 returns 是一个 Promise,很容易用 response 来动作。

但现在我正在研究 redux-saga,并试图弄清楚如何才能获得与上述代码相同的结果。由于 Saga 的 运行 在不同的线程上,我无法从上面的查询中获得回调。所以我只想知道你们是怎么做到的。或者在使用 redux-saga 时处理回调的最佳方法是什么? 调度操作如下所示:

this.props.actions.login(credentials);

和传奇

function* login(action) {
  try {
    const state = yield select();
    const token = state.authReducer.token;
    const response = yield call(API.login,action.params,token);
    yield put({type: ACTION_TYPES.LOGIN_SUCCESS, payload:response.data});
    yield call(setItem,AUTH_STORAGE_KEY,response.data.api_token);
  } catch (error) {
    yield put({type: ACTION_TYPES.LOGIN_FAILURE, error})
  }
}

传奇监视器

export function* loginMonitor() {
  yield takeLatest(ACTION_TYPES.LOGIN_REQUEST,login);
}

动作创作者

function login(params) {
  return {
    type: ACTION_TYPES.LOGIN_REQUEST,
    params
  }
}

我认为您应该向登录生成器添加重定向和警报。这样所有的逻辑都在 saga 中并且仍然很容易测试。所以基本上你的登录传奇是这样的:

function* login(action) {
  try {
    const state = yield select();
    const token = state.authReducer.token;
    const response = yield call(API.login,action.params,token);
    yield put({type: ACTION_TYPES.LOGIN_SUCCESS, payload:response.data});
    yield call(setItem,AUTH_STORAGE_KEY,response.data.api_token);
    yield call(redirectToHomePage); // add this...
  } catch (error) {
    yield put({type: ACTION_TYPES.LOGIN_FAILURE, error});
    yield call(alertError); // and this
  }
}

我整天都在研究这些东西,从 thunk 切换到 redux-saga

我也有很多这样的代码

this.props.actions.login(credentials)
.then((success)=>redirectToHomePage)
.catch((error)=>alertError);

可以使用 thunk + saga

function login(params) {
  return (dispatch) => {
    return new Promise((resolve, reject) => {
      dispatch({
        type: ACTION_TYPES.LOGIN_REQUEST,
        params,
        resolve, 
        reject
      })
    }
  }
}

然后在 saga land 你可以做类似的事情

function* login(action) {
  let response = yourApi.request('http://www.urthing.com/login')
  if (response.success) {
    action.resolve(response.success) // or whatever
  } else { action.reject() }
}

您只需将有关成功和错误回调函数的额外信息传递到有效负载本身即可。因为,redux 模式以一种完全分离的方式工作。

this.props.actions.login({
   credentials,
   successCb: success => redirectToHomePage)
   errorCb: error => alertError)
 });

在 saga 中,您可以从有效负载中解构这些回调,并根据您的程序流程运行非常轻松地解构它们。

您的来电:

this.props.addCutCallback(currentTime, callback);

您传递给 connect() 函数的映射:

const mapDispatchToProps = (dispatch) => ({
  addCutCallback: (time, callback) =>
    dispatch(ACTIONS.addCutCallback(time, callback)),
});

export default connect(mapStateToProps, mapDispatchToProps)(Home);

你的故事:

import {put, takeEvery, all, select} from 'redux-saga/effects';
import * as Actions from './../actions';

const getCuts = (state) => state.cuts;

function* addCutSaga({time, callback}) {
  yield put({type: Actions.ADD_CUT, time});
  const cuts = yield select(getCuts);
  callback(cuts);
}

function* cutsSaga() {
  yield takeEvery(Actions.ADD_CUT_CALLBACK, addCutSaga);
}

export default function* rootSaga() {
  yield all([cutsSaga()]);
}

我发现更优雅的方法是简单地使用 useEffect.

//selectors.ts

export function hasAuthError(state: RootState): boolean {
  return state.auth.error;
}

export function getAuthMessage(state: RootState): string {
  return state.auth.message;
}
// some react component

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Toast from 'react-native-toast-message';
import { getAuthMessage, hasAuthError } from 'src/store/auth/selectors';

  ...

  const message = useSelector(getAuthMessage);
  const hasError = useSelector(hasAuthError)

  ...

  useEffect(() => {
    if (hasError) {
      Toast.show({
        type: 'error',
        text2: message,
        topOffset: 50,
        visibilityTime: 5000
      });
    }
  }, [message, hasError]);