【问题标题】:How to prevent redux-saga from calling my api call twice?如何防止 redux-saga 调用我的 api 调用两次?
【发布时间】:2019-12-01 01:29:32
【问题描述】:

在新用户登录我的应用时,我正在使用 redux-saga 处理我的 API 调用。但是,我的 saga watcher 调用了我的 API 两次。第一次,API 正确获取数据,第二次,它给了我一个错误 400。

我尝试使用 takeLatest 传奇效果。没用。

我也尝试了这种自定义传奇效果。 import { call, fork, take } from "redux-saga/effects";

export function* takeOneAndBlock(pattern, worker, ...args) {
  const task = yield fork(function*() {
    while (true) {
      const action = yield take(pattern);
      yield call(worker, ...args, action);
    }
  });
  return task;
}

它可以防止 API 渲染两次。但阻止我的会话和令牌管理的所有其他调用

这是我登录传奇的代码。

signInSaga.js

/* eslint-disable no-unused-vars */

import { put, call, takeEvery, delay } from "redux-saga/effects";


import {
  AUTHENTICATE_USER,
  CLEAR_ERRORS,
  SET_ERRORS,
  AUTH_INITIATE_LOGOUT,
  AUTH_CHECK_TIMEOUT
} from "../actions/types";
import * as actions from "../actions/signInActions";
//import qs library from es6 to stringify form data
import qs from "qs";
// import configured axios from "axios_api file";
import api from "../apis/axios_api";

/** function that returns data for token and session management */
function getToken() {
  //TODO: Add logic to get jwt token using username and password
  const expiresIn = 60 * 1000; // in milli seconds
  const expirationDate = new Date(new Date().getTime() + expiresIn);
  return {
    token: "fakeToken",
    userId: "fakeUserId",
    expirationDate,
    expiresIn
  };
}

/** function that returns an axios call */
function loginApi(loginData) {
  //Axios doesn't stringify form data by default.
  //Hence, qs library is used to stringify upcoming redux-form data.

  return api.post("/login", qs.stringify(loginData));
}

/** saga worker that is responsible for the side effects */
function* loginEffectSaga(action) {
  try {
    // data that is obtained after axios call
    let history = action.history;

    let { data } = yield call(loginApi, action.payload);

    // dispatch authenticate user action to change redux state
    yield put(actions.authenticateUser(data, history));

    //to set and dispatch session and token management action creators
    const tokenResponse = getToken();
    yield localStorage.setItem("token", tokenResponse.token);
    yield localStorage.setItem("expirationDate", tokenResponse.expirationDate);
    yield localStorage.setItem("userId", tokenResponse.userId);

    yield put(actions.authSuccess(tokenResponse.token, tokenResponse.userId));
    yield put(actions.checkAuthTimeout(tokenResponse.expiresIn));

    //dispatch clear_errors action creator to remove any previous set errors
    yield put({ type: CLEAR_ERRORS });

    // redirect to dashboard route after successful Login

    history.push("/dashboard");
  } catch (e) {
    // catch error on a bad axios call and dispatch set_errors action creator
    yield put({ type: SET_ERRORS, payload: e.response.data });
    console.log("errors", e.response);
  }
}

/**
 * saga watcher that is triggered when dispatching action of type
 * 'AUTH_CHECK_TIMEOUT' and inturn it fires off AUTH_INITIATE_LOGOUT action creator
 */
function* checkAuthTimeout(action) {
  yield delay(action.expirationTime);
  yield put(actions.logout());
}

/**
 * saga watcher that is triggered when dispatching action of type
 * 'AUTH_INITIATE_LOGOUT'
 */
function* logout(action) {
  yield localStorage.removeItem("token");
  yield localStorage.removeItem("expirationDate");
  yield localStorage.removeItem("userId");
  yield put(actions.logoutSucceed());
}

/**
 * saga watcher that is triggered when dispatching action of type
 * 'SET_AUTHENTICATED'
 */
export function* loginWatcherSaga() {
  yield takeEvery(AUTHENTICATE_USER, loginEffectSaga);
}

export function* logoutWatcherSaga() {
  yield takeEvery(AUTH_INITIATE_LOGOUT, logout);
}

export function* checkAuthWatcherSaga() {
  yield takeEvery(AUTH_CHECK_TIMEOUT, checkAuthTimeout);
}

//rootSaga.js

import { all, fork } from "redux-saga/effects";
import * as signInSagas from "./signInSaga";
import * as signUpSagas from "./signUpSaga";
import * as forgotPasswordSagas from "./forgotPasswordSaga";

// import watchers from other files
export default function* rootSaga() {
  yield all(
    [
      ...Object.values(signInSagas),
      ...Object.values(signUpSagas),
      ...Object.values(forgotPasswordSagas)
    ].map(fork)
  );
}

//index.js

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const sagaMiddleware = createSagaMiddleware();

const store = createStore(
  rootReducer,
  composeEnhancers(applyMiddleware(sagaMiddleware))
);
sagaMiddleware.run(rootSaga);

【问题讨论】:

    标签: javascript reactjs react-redux redux-saga


    【解决方案1】:

    我不认为你的传奇逻辑是错误的。也许调度动作的部分可能是错误的。

    或者您可以像这样更改登录观察者,

    export function* loginWatcherSaga() {
      while(true) {
        const authAction = yield take(AUTHENTICATE_USER);
        yield fork(loginEffectSaga, authAction);
        const action = yield take([AUTHENTICATE_SUCCESS, AUTHENTICATE_FAILURE]);
        // ... your logic
    }
    

    这样,第二个take 保证阻止其他操作类型,直到 `loginEffectSaga' 完成。

    如果loginEffectSaga之后不需要其他逻辑,可以将fork改为call

    【讨论】:

      最近更新 更多