Search code examples
reactjsreact-reduxredux-saga

Saga is not triggered the second time an action is dispatched


I'm trying to implement a cancel task effect using redux-saga. It runs okay on the first dispatch but on the second time, it does nothing. It seems the saga has ended or something. Here's my code:

import { all, put, fork, take, cancel, cancelled } from "redux-saga/effects";

const searchUsers = function* () {
  try {
    yield new Promise((resolve) => setTimeout(resolve, 1500));
    const users = ["Bill Gates"];
    yield put({ type: "SEARCH_USERS_SUCCESS", users });
  } catch (e) {
    // log error
  } finally {
    if (yield cancelled()) {
      console.log("search was cancelled");
    }
  }
};

const searchUsersSaga = function* () {
  const searchAction = yield take("SEARCH_USERS");
  const searchTask = yield fork(searchUsers, searchAction.query);
  const cancleAction = yield take("SEARCH_USERS_CANCEL");

  if (cancleAction.type === "SEARCH_USERS_CANCEL") {
    yield cancel(searchTask);
  }
};

const rootSaga = function* saga() {
  yield all([searchUsersSaga].map(fork));
};

export default rootSaga;

I created a complete code here: https://codesandbox.io/s/new-fast-snhr0?file=/src/index.js


Solution

  • With take effect, saga call action = getNextAction() which will resolve when the action is dispatched, therefore your searchUsersSaga should in the while(true) loop.

    One more problem, in case SEARCH_USERS_SUCCESS, searchUsersSaga will wait for SEARCH_USERS_CANCEL and it can also block the flow, you should dispatch another action to handle that case.

    const searchUsers = function* () {
      try {
        yield new Promise((resolve) => setTimeout(resolve, 1500));
        const users = ["Bill Gates"];
        yield put({ type: "SEARCH_USERS_SUCCESS", users });
      } catch (e) {
        // log error
      } finally {
        yield put({ type: "SEARCH_USERS_END" });
        if (yield cancelled()) {
          console.log("search was cancelled");
        }
      }
    };
    
    const searchUsersSaga = function* () {
      while (true) {
        const searchAction = yield take("SEARCH_USERS");
        const searchTask = yield fork(searchUsers, searchAction.query);
        const cancleAction = yield take([
          "SEARCH_USERS_CANCEL",
          "SEARCH_USERS_END"
        ]);
    
        if (cancleAction.type === "SEARCH_USERS_CANCEL") {
          yield cancel(searchTask);
        }
      }
    };
    
    export const usersReducer = (state = initialState, action) => {
      switch (action.type) {
        ...
        case "SEARCH_USERS_END":
          return {
            ...state,
            isSearching: false
          };
        ...
      }
    };
    
    

    https://codesandbox.io/s/sad-cori-yodq0