Redux Saga Effects

Reading Time: 6 minutes

Effects are the kind of utility that’s provided by the redux-saga package. when u invokes effects it returns object contains instructions which redux-saga interprets. The effects don’t have any special property outside the redux-saga application because it’s redux-saga that generates side effects like API call database or something else, The effect itself doesn’t actually do anything.

If you don’t know how to create a Saga, follow my previous blog.

redux-saga-effect

Let’s talk about some of the side effects available in redux-saga:

Take:  It is very simple effects of redux-saga, which pauses between concurrent lines of code until the action we decide is dispatched. and resumes when the specified action is dispatched. This effect does not create additional effects i.e only one thread multiple actions doesn’t mean multiple responses. Since we are using yield keyword to do this whatever properties of action are passed to a running saga when the action occurs.

eg: Now you can use take effect by calling effect.take() and we can pass any string here like “MY_Action” and what it returns is an object like this:

{@@redux-saga/IO: true, TAKE: {…}}
@@redux-saga/IO: true
TAKE: {pattern: “MY_ACTION”}

So now we will define another generator say mySaga so we start with the console.log and then we’ll yield an effect take() and we’ll wait for an action called SET_STATE and we store what yielded in a variable and then we’ll end the code by console logging “got the state”.

let mySaga = function*() {
    console.info(“Saga begins!”);
    const state = yield effects.take(“SET_STATE”);
    console.info(“Got State…”, state);
}

Now, we will run the command run(mySaga) to execute saga it will return the output like shown below:

Saga Begins!  // console output
{@@redux-saga/TASK: true, id: 1, name: “mySaga”, cont: undefined, joiners: Array(0), …}

So by observing this output, you will notice that after using take() method the code pauses which is its behavior and it will not print and another console so to achieve that we have to dispatch an action setState with some value using which it will start resuming the code with another output.
eg:

dispatch({type: “SET_STATE”, value: 42});
output: {type: “SET_STATE”, value: 42}

PUT: It is another effect which won’t do anything unless it is inside redux, it is, nothing but the dispatch action, which immediately dispatches the action throughout the app, also it didn’t pause the execution of the code. It is dispatch similar we have in redux-thunk and react-redux.

eg: we will write a saga first to consume the action and we can use our existing saga named mySaga from our previous example of taking and run(mySaga) again, after running mySaga we will create another saga i.e; put saga.

let putSaga = function*() {
    yield effects.put({type: “SET_STATE”, value: 42});
}

We will run putSaga using command run(putSaga), you noticed that mySaga is getting the value from putSaga.

Output:
Got State…   //console.log
{type: “SET_STATE”, value: 42, @@redux-saga/SAGA_ACTION: true}
{@@redux-saga/TASK: true, id: 3, name: “putSaga”, cont: undefined, joiners: null, …}

Call: It is another method that is simply used to call the specified method, it is equivalent to invoking a method directly. so you must have to wonder what is the significance of this effect than?
Basically, we use call effect to yielding a method directly, we have so because this method is called during test. this method is also worked within a saga.

eg: We will just create a simple function with the console to see logs.

let fn =() => {
    console.info(“Calling the function”)
}

Now, we will create a saga that will just yield that fn and you will see after running the saga like this run(saga) it will print the console value of the method(fn).

eg:

run(saga);

Output:

Calling the function // console log
{@@redux-saga/TASK: true, id: 1, name: “saga”, cont: undefined, joiners: null, …}

We will write the same method with effect call as shown below:

let saga = function*() {
    yield effects.call(fn())
}

Output:

{@@redux-saga/TASK: true, id: 3, name: “saga”, cont: undefined, joiners: null, …}

We will see that it will display the same output similar to the method invoked simply like I said to call() method is similar to the normal invocation.

Fork: Fork is pretty complex to understand, the fork is also simply used to call a method, you passed a method and method is invoked. However, you can’t capture the variable that was yielded from the forked application because the caller continues to run instantly without pausing. when we used to call the call fn stops until the function or generators completes.
When we use a fork the child processes are dependent on the parent processes, i.e if the parent process fails or canceled similarly all the forked processes are canceled. The forked method can use finally block which is similar to try/catch block which tells if they’ve been canceled or if a parent has any error.

Here, we will create a method that loops and creates a fork with another method, note that first thread never pauses ad also notes that ore instances of second thread continuously created.

eg: Now will define a saga in which it will loop and every time it will loop it fork the function ‘FN’.

function* fn() {
while(true) {
console.log(‘FN’);
yield delay(1000);
}
}

And, now will right saga with a delay of 500ms and then will run saga using run command run(saga), and after you run saga you’ll be noticed that it will run console ‘FN’ many times but all the forks process are run independently and at some point of time you have to stop your page to get rid of this.

TakeEvery: It is a combination of take and fork method in which every time a method is invoked a specified action is dispatched and fork a new child process to handle it. And, unlike take but like folk when you called takeEvery() you main thread continues to execute.

For this method, we will create a saga that invokes a method each time a specified action is dispatched. you’ll notice how multiple threads can be created and the thread where takeEvery is called resumes immediately.

eg: Now we will create a process which will loop all the process after 1000ms.

let process = function*() {
    while(true) {
    console.log(‘Process loop’);
    yield delay(1000);
    }
}

And, then we will create a saga for processes to run:

let processSaga = function*(){
    yield effects.takeEvery(“START_PROCESS”, process)
    console.log(‘saga got to end’);
}

It will generate an output like shown below in which you’ve noticed that the process didn’t call much time that is because the saga is still waiting to dispatch the action so let dispatch it manually.

run(processSaga);
saga got to end    //console log

{@@redux-saga/TASK: true, id: 1135, name: “processSaga”, cont: undefined, joiners: Array(0), …}

After dispatch you will notice the process loop runs after evert 1000ms.
dispatch({type: “START_PROCESS”});

Spawn: Spawn is somehow similar to the fork effect, so when you call a spawned process it will create a new process like fork, the caller is not interested in its processing and goes down to the next line. Now the new process is not the child process of a caller and it will not be canceled, So if the caller errors or is the new process is not effected this is the difference between spawn and fork.

In this case, we will create a saga which calls a process and error shortly after, note how the callee process continues to run replace spawn with fork and not how child process is stopped on error.

Let’s start with creating a process just like we did in this blog,

let process = function*() {
let timesLoop = 0;
while(true) {
console.info(‘loop: ‘ + timesLoop++);
yield delay(500);
}
}

Now will define a saga in which we spawn the process then it will delay for 2 seconds.

let prosaga = function*(){
    yield effects.spawn(process);
    yield delay(2000);
}

Finally,  it will throw an error:

prosaga = function*(){
    yield effects.spawn(process);
    yield delay(2000);
    throw new Error();
}

Now, we run saga we’ll notice that we got an uncaught error at saga as expected but the loops keep on going that’s because the spawn event isn’t canceled.
Try the same thing with Fork you will be noticed that the child process is canceled after an error is thrown.

all: The last and most important effect I am covering in this article is ALL effect. It is a powerful effect that combines all the take statements into one. when all yielded to the code execution will stop and will only resume when all the actions that are specified in all command are dispatched and order doesn’t make any difference to it.

eg:

yield all([
     take(‘SET_ACTION_1’);
     take(‘SET_ACTION_2’);
]);

I hope after reading this blog you will be able to gain basic knowledge of these effects, I would just to spend some time of your on each effect and play around it to get more understanding.

Thanks For Reading!!!

Written by 

Nitin Arora is a Software Consultant at Knoldus Software LLP. He has done MCA from the Banarsidas Chandiwala Institute of Information technology, Delhi(GGSIPU). He has a graduation degree in BCA from Jamia Hamdard. He has a sound knowledge of various programming languages like C, C++, Java. Also has a deep interest in frontend development like Html, CSS, Angular, Javascript, ionic, react with redux, bootstrap. He is currently working in frontend technologies like React, Html, SCSS, Bootstrap, and Typescript. He is a focused, hardworking, team-oriented member and always exploring new Technologies, His hobbies are to play cricket, volleyball, and do coding.