I would like to share an article written by one of our developers. It can be considered as some kind of tutorial. Talking about everyday use technology.
Redux saga is a middleware between an application and redux store, that is handled by redux actions. This means it can listen to actions, intercept actions it is interested in, and replace them with other actions. This may be useful for plenty of things. In particular, using sagas you can keep your components as simple as possible and move all the logic to sagas.
When you start working with Redux saga, it seems quite natural to write sagas that describe the behavior of a single component or a single page. And it works fine in simple applications that have two or three pages and everything is loaded synchronously. But when a project grows, this may lead you to a mess in the sagas department, where sagas don’t have clear purposes and duplicate each other in many ways. It will be difficult to manage, debug, and even make small changes.
The thing is that work done in saga should be coordinated. After some time of struggling with this mess and finally reading the documentation, we in DashBouquet came up with a new approach, which has always been there. Describe a full flow in a saga. This is what the documentation implies, you should just read carefully. Let’s see what it looks like.
It looks like a flow, doesn't it? Sure, describing full flow in saga doesn’t mean that there should be only one saga. The point is that everything is handled from one place.
Now, let’s take a look at an actual example. Say in our app a user is able to fill up a job application and send it to the server for further processing. We want to create a saga that will describe full flow of this process.
Everything starts on /create_application_page route when a user clicks an “Apply for a job” button and is redirected to the application form.
Here CREATE_APPLICATION is an action dispatched when a user clicks “Apply for a job.” Then he is redirected to another page with a form. After the user fills up the form and clicks Submit or Cancel, he is redirected further depending on which action was dispatched. And data is sent to the server only if the user clicks Submit.
Also, the user may change his mind in the middle of filling up the form and just go by another link without clicking “Cancel.” In this case, if he later comes back to /create_application_page where the saga is injected asynchronously, it will be injected one more time, and the data on the server will be created twice, while the user intended to do it only once. We certainly don’t want that, so we need to cancel the task on LOCATION_CHANGE dispatched by redux-router, hence checking the pathname in watchCreateApplication.
And just to make things a bit more complicated, we assume, that in the middle of filling up the form, the user can call a dialog with another form, in which he can choose, say, a country and a city he is applying from and submit this data to the server. We can do this by simply forking another saga that would be quite similar to createApplication saga:
Here we handle the dialog with showDialog and closeDialog sagas. In two words, they should set some value in the store to tell a page, if the dialog has to be shown or hidden at the moment. In more detail, it is a subject for another article.
It is important not to forget to cancel the forked tasks. If at some point you find an avalanche of requests going to the server when it was supposed to be only one or two, you probably missed this somewhere.
It is a quite simple example. A flow may be much more complicated, with any amount of sub-flows (though it might be a bad idea from a UX point of view). But anyway, what this approach gives is that you can build your flow step by step. You can make changes later quite easily and they will not lead to unexpected behavior from other sagas. You can make it more complicated if you need to, but no matter how many forks your flow has you never get lost in it.