How to cancel saga task when specific action is di

2019-08-17 23:23发布

I'm trying to figure out how to solve my problem, but I didn't find good enough solution on the web.

I need to cancel checkAuth and logout tasks when action LoginActionType.REQUEST_SEND is dispatched.

function* handleLoginFetch(userCredentialsAction: PayloadAction<LoginActionType, UserCredentials>) {
    try {
        const response: AxiosResponse<AuthResponse> = yield call($http.put, '/users/login', userCredentialsAction.payload);

        if (response.status === HttpStatusCode.OK) {
            yield put(login.success(response.data.user));
        }
    } catch (error) {
        yield put(login.failure());
    }
}

function* handleCheckAuthFetch() {
    try {
        const response: AxiosResponse<AuthResponse> = yield call($http.get, '/users/logged-user', {
            params: { 'include': 'user_user_permissions' }
        });

        if (response.status === HttpStatusCode.OK) {
            if (yield select(getUserLoggedIn)) {
                yield put(login.success(response.data.user));
            } else {
                yield put(checkLocalAuth.success(response.data.user));
            }
        }
    } catch (error) {
        yield put(checkLocalAuth.failure());
    }
}

function* handleLogoutFetch() {
    try {
        const response: AxiosResponse = yield call($http.put, '/users/logout');

        if (response.status === HttpStatusCode.OK) {
            yield put(logout.success());
        }
    } catch (error) {
        yield put(logout.failure())
    }
}

export default function* userSaga() {
    yield takeLatest(LoginActionType.REQUEST_SEND, handleLoginFetch);
    yield takeLatest(CheckLocalAuthActionType.REQUEST_SEND, handleCheckAuthFetch);
    yield takeEvery(LogoutActionType.REQUEST_SEND, handleLogoutFetch);
}

标签: redux-saga
2条回答
放荡不羁爱自由
2楼-- · 2019-08-17 23:41

You could:

  • "implement" by yourself what takeLatest does. The docs says

Spawns a saga on each action dispatched to the Store that matches pattern. And automatically cancels any previous saga task started previously if it's still running.

So instead of writing yield takeLatest(CheckLocalAuthActionType.REQUEST_SEND, handleCheckAuthFetch); you can write a function that does the same

export default function* forkHandleCheckAuthFetch() {
  let task;

  while (true) {
    // this loop stops here until one of the actions is triggered
    const action = yield take([CheckLocalAuthActionType.REQUEST_SEND, LoginActionType.REQUEST_SEND]);

    // both the actions cancel the previous forked task (similar to what `takeLatest does`)
    if (task) {
      cancel(task);
    }

    // only the "correct" action starts the desided behaviour
    if (action.type === CheckLocalAuthActionType.REQUEST_SEND) {
       // a fork can be cancelled...
      task = yield fork(handleCheckAuthFetch, action);
    }
  }
}

(the takeLatest function spawns a saga while my implementation forks the saga but don't worry about it at the moment)

  • the same we can do for the handleLogoutFetch
export default function* forkHandleLogoutFetch() {
  let task;
  while (true) {
    const action = yield take([LogoutActionType.REQUEST_SEND, LoginActionType.REQUEST_SEND]);
    if (task) {
      cancel(task);
    }
    if (action.type === CheckLocalAuthActionType.REQUEST_SEND) {
      task = yield fork(handleLogoutFetch, action);
    }
  }
}
  • and then change your userSaga to
export default function* userSaga() {
  yield forkHandleCheckAuthFetch();
  yield forkHandleLogoutFetch();
  yield takeLatest(LoginActionType.REQUEST_SEND, handleLoginFetch);
}

So, now:

  • your CheckLocalAuthActionType.REQUEST_SEND action triggers the handleCheckAuthFetch as it did before my implementation
  • your LogoutActionType.REQUEST_SEND action triggers the handleLogoutFetch as it did before my implementation
  • the LoginActionType.REQUEST_SEND action cancels every running handleCheckAuthFetch and handleLogoutFetch sagas

That's what I do in my projects, it's up to you to abstract them to a utility function, I care that you understand how it works and how to implement it

查看更多
干净又极端
3楼-- · 2019-08-17 23:57

NoriSte's answer really helped me to understand how to solve my issue. I studied Saga documentation a little bit more and I ended up with this solution.

export default function* userSaga() {
    const checkAuthTask = yield takeLatest(CheckLocalAuthActionType.REQUEST_SEND, handleCheckAuthFetch);
    const logoutTask = yield takeEvery(LogoutActionType.REQUEST_SEND, handleLogoutFetch);

    yield takeLatest(LoginActionType.REQUEST_SEND, function*(userCredentialsAction: PayloadAction<LoginActionType, UserCredentials>) {
        if(checkAuthTask) yield cancel(checkAuthTask);
        if(logoutTask) yield cancel(logoutTask);

        yield fork(handleLoginFetch, userCredentialsAction);
    });
}
查看更多
登录 后发表回答