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?
Redux Saga is a middleware library used to allow a Redux store to interact with resources outside of itself asynchronously. This includes making HTTP requests to external services, accessing browser storage, and executing I/O operations. These operations are also known as side effects.
The fork effect always wins the race immediately. On the other hand, fork effects in a race effect is most likely a bug. In the above code, since fork effects are non-blocking, they will always win the race immediately.
If any forked task raises an uncaught error, then the parent task will abort with the child Error, and the whole Parent's execution tree (i.e. forked tasks + the main task represented by the parent's body if it's still running) will be cancelled.
Such a powerful & elegant tool as Redux-Saga, a Redux side effect manager, is said to be deprecated, and no longer being maintained, starting from Jan 27, 2021.
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:
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.
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With