如何防止 redux-saga 调用我的 api 调用两次?
Posted
技术标签:
【中文标题】如何防止 redux-saga 调用我的 api 调用两次?【英文标题】:How to prevent redux-saga from calling my api call twice? 【发布时间】: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);
【问题讨论】:
【参考方案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
。
【讨论】:
以上是关于如何防止 redux-saga 调用我的 api 调用两次?的主要内容,如果未能解决你的问题,请参考以下文章
redux-saga api yield 调用有效,但没有返回数据