anlogg
anlogg

Reputation: 1090

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

Upvotes: 3

Views: 1303

Answers (1)

tuan.tran
tuan.tran

Reputation: 1881

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

Upvotes: 2

Related Questions