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?
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.