redux-saga when to use fork?
Asked Answered
T

2

56

what would be the difference between the two approaches below?

export function* watchLoginUser() {
  yield takeEvery(USER_LOGIN, loginUser)
}
export function* watchLogoutUser() {
  yield takeEvery(USER_LOGOUT, logoutUser)
}
export function* watchGetParties() {
  yield takeEvery(PARTIES_GET, getParties)
}
export default function* root() {
  yield [
    fork(watchLoginUser),
    fork(watchLogoutUser),
    fork(watchGetParties)
  ]
}
export default function* root() {
  yield [
    takeEvery(USER_LOGIN, loginUser),
    takeEvery(USER_LOGOUT, logoutUser),
    takeEvery(PARTIES_GET, getParties)
  ]
}

When do I need to use fork and when not?

Transvalue answered 21/3, 2017 at 21:34 Comment(0)
P
106

In general, fork is useful when a saga needs to start a non-blocking task. Non-blocking here means: the caller starts the task and continues executing without waiting for it to complete.

There is a variety of situations where this can be useful, but the 2 main ones are:

  • grouping sagas by logical domain
  • keeping a reference to a task in order to be able to cancel/join it

Your top-level saga can be an example of the first use-case. You'll likely have something like:

yield fork(authSaga);
yield fork(myDomainSpecificSaga);
// you could use here something like yield [];
// but it wouldn't make any difference here

Where authSaga will likely include things like:

yield takeEvery(USER_REQUESTED_LOGIN, authenticateUser);
yield takeEvery(USER_REQUESTED_LOGOUT, logoutUser);

You can see that this example is equivalent to what you suggested, calling with fork a saga yielding a takeEvery call. But in practice, you only need to do this for code organisation purposes. takeEvery is itself a forked task, so in most cases, this would be uselessly redundant.

An example of the second use-case would be something like:

yield take(USER_WAS_AUTHENTICATED);
const task = yield fork(monitorUserProfileUpdates);
yield take(USER_SIGNED_OUT);
yield cancel(task);

You can see in this example that the monitorUserProfileUpdates will execute while the caller saga resumes, and gets to wait to the USER_SIGNED_OUT action to be dispatched. It can in addition keep a reference to it in order to cancel it when needed.

For the sake of completeness, there is another way to start non-blocking calls: spawn. fork and spawn differ in how errors and cancellations bubble from child to parent saga.

Punctual answered 22/3, 2017 at 19:49 Comment(0)
T
3

Usually fork become more useful for some cases that has multiple dispatches of API calls, the reason is you can reject the those fetches by instantiating the cancel from the task, e.g. cancel(task1);

Useful if the end-user forcefully exit the application or if one of the tasks was failed that make a problem from your instructions, strategy and logic and it might be reasonable to cancel or terminate the current processing tasks on your saga;

There are 2 ways to cancel the task

base from the documentation of redux-saga Non-Blocking effect cancellation

import { take, put, call, fork, cancel } from 'redux-saga/effects'

// ...

function* loginFlow() {
  while (true) {
    const {user, password} = yield take('LOGIN_REQUEST')
    // Non-Blocking Effect which is the fork
    const task = yield fork(authorize, user, password)
    const action = yield take(['LOGOUT', 'LOGIN_ERROR'])
    if (action.type === 'LOGOUT'){
      //cancel the task
      yield cancel(task)
      yield call(Api.clearItem, 'token')
    }
  }
}

OR


import {call, put, fork, delay} from 'redux-saga/effects';
import someAction from 'action/someAction';

function* fetchAll() {
  yield fork(fetcher, 'users');
  yield fork(fetcher, 'posts');
  yield fork(fetcher, 'comments');
  yield delay(1500);
}

function* fetcher(endpoint) {
  const res = yield call(fetchAPI, endpoint);
  if (!res.status) {
    throw new Error(`Error: ${res.error}`);
  }
  yield put(someAction({payload: res.payload}));
}

function* worker() {
  try {
    yield call(fetchAll);
  } catch (err) {
    // handle fetchAll errors
  }
}

function* watcher() {
  yield takeEvery(BLOGS.PUSH, worker);
}

Your welcome :)

Tuckerbag answered 7/7, 2021 at 20:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.