Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the point of watchers in redux-saga?

I just got into redux-saga and I'm a bit confused on Watchers and Generators

Take for example this code below, that is the entry-point of my sagas file

function* employeesSaga() {
    yield all([
        takeEvery(getType(employeeActions.login.request), handleLoginRequest),
        takeEvery(getType(employeeActions.verifyLogin.request), handleVerifyLoginRequest),
        takeEvery(getType(employeeActions.logout), handleLogout)
    ]);
}

I am directly wiring each redux call to the corresponding handler.

But some people use watchers, and then they call the handler in that generator. What is the purpose of doing that? Should I use that pattern?

Also, I noticed some people wrap their entire handler with a while(true), is that necessary? Because my code works fine without that too...

like image 530
Sai Datta Avatar asked Nov 16 '18 06:11

Sai Datta


People also ask

What's the point of Redux saga?

redux-saga is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures.

What is the use of takeEvery in Saga?

In the above example, takeEvery allows multiple fetchData instances to be started concurrently. At a given moment, we can start a new fetchData task while there are still one or more previous fetchData tasks which have not yet terminated.

What is the main task of takeLatest?

takeLatest(pattern, saga, ... And automatically cancels any previous saga task started previously if it's still running. Each time an action is dispatched to the store. And if this action matches pattern , takeLatest starts a new saga task in the background.

How do I read Redux saga?

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.


1 Answers

On the first question

It may be just a matter of readability.

Watchers aren't a real "pattern", they just make your code more explicit about its intentions:

function* watchLoginRequest() {
    yield takeEvery(getType(employeeActions.login.request), handleLoginRequest)
}

function* watchVerifyLoginRequest() {
    yield takeEvery(getType(employeeActions.verifyLogin.request), handleVerifyLoginRequest)
}

function* watchLogout() {
    yield takeEvery(getType(employeeActions.logout), handleLogout)
}

function* startWatchers() {
    yield call(watchLoginRequest)
    yield call(watchVerifyLoginRequest)
    yield call(watchLogout)
}

function* employeesSaga() {
    yield call(startWatchers)
}

Second question

Probably you're talking about this kind of flow:

function* watchLoginRequest() {
    while (true) {
        const action = yield take(getType(employeeActions.login.request))
        yield call(handleLoginRequest, action)
    }
}

The difference:

  • The while(true)-take-call flow does not allow executing two instances of a single handler concurrently. It takes one action, then blocks on the call, until the handleLoginRequest() has finished. If the user clicks the login button before the handler has completed, the correnponding actions are missed.

  • The takeEvery() flow does allow concurrent handler execution. This might not be what you want.

Redux-saga docs tell how takeEvery() is implemented under the hood:

const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() {
  while (true) {
    const action = yield take(patternOrChannel)
    yield fork(saga, ...args.concat(action))
  }
})

You see, takeEvery() is non-blocking itself (a fork), and it executes the handler in a non-blocking way (a fork).

A third option

There is also takeLatest(), which does allow concurrent handler execution, but if there is a previous instance of a handler executing, it cancels it. Redux-saga docs provide its internal implementation, too.

The while(true)-take-call is the best as a login flow, I think. You probably do not want concurrent logins. If you block concurrent logins on UI level, though, these flows are equivalent, but while(true)-take-call is the most explicit and readable.

like image 88
Andrey Moiseev Avatar answered Sep 20 '22 23:09

Andrey Moiseev