Introduction to Redux - Saga
What is Redux?
Before answering that question we must talk about the state in JavaScript web application. Here is a simple use case :
There is an application where you can insert Item data. Once the user clicks on “Add new” button it will open a drawer with a form and when you click on the “close” button on the drawer it should be hide.
Even for this simple scenario there is a state to deal with.
Here is the default state before user clicks on the “Add New” button.
Once the user clicks on the button the visible state change to “true”
Again if the user clicks on the “Close” button visible state goes back to false state.
So do we need redux to handle this simple thing? No! Can you imagine how much state a web application has? and that’s what redux is going to solve.
What is Saga?
Saga is a middleware library which is allowed redux store to interact with resources outside of itself asynchronously. Which means making HTTP requests and call external API services, accessing browser storage and executing I/O operations. Normally we call these operations as side effects. So basically redux saga helps to manage these side effects in a simple way.
Sagas run in the background right after the app is launched. Sagas observe all actions that the store dispatches and decide what to do with them.
When we are using sagas, we have few benefits too. Testing is becoming much easier and test cases become simple without needing to mock async behaviour. Also the code is much more readable. However, redux-saga also brings in a lot of added complexity and additional dependencies.
Let’s talk more about redux
Well, to be honest when I learn redux for the first time I was really confused. Too much of new terminology such as actions, reducers, middleware, store etc and I had no clue how this things connect to each other.
But don’t worry, eventually you will realise how things work together.
Redux Store
Let’s get back to the topic. So Redux Store holds all of the application’s state. So let’s create a store and look how it’s work.
npm i redux react-redux
After installing those packages, create a folder called store inside the src folder. Then add following code in index.js file.
import { createStore } from 'redux'
import rootReducer from './root.reducer.js'
const store = createStore(rootReducer)export default store
Redux Reducers
A reducer is a JavaScript function which takes two parameters called state and action.
In a typical React component the local state might be mutated in place. In Redux you’re not allowed to do that. The third principle of Redux (as outlined by its creator) prescribes that the state is immutable and cannot change in place.
Below is an example reducer. Here we have pass the initial state. So this reducer does nothing other than returning the initial state.
const initialState = {
data: []
}
function exampleReducer(state = initialState, action) {
return state
}
export default exampleReducer
Confused? yah I was too. But you will understand everything please continue. We will create a meaning full reducer file later.
Redux Actions & Constants
Reducers are one of the main concept in redux because it generate the state of the application. But how does it knows when to generate the next state? for that we need Actions and constants.
The second principle of Redux says the only way to change the state is by sending a signal to the store. This signal is an action. So “dispatching an action” means sending out a signal to the store.
Let’s have a look on constants. I’m going to develop a simple weather dashboard using React — Redux saga. So create a folder called weather inside store folder and create a file called weather.constants.js
This is the action. As you can see it is a JavaScript object with two properties: type and payload.
The type property means how the state should change and it is required by Redux.
The payload property means what should change in the state and it is optional. which means if you don’t have new data to save in the store you can omit payload.
Still wondering why do we need constants? see the type property takes a string. Strings are prone to typos and duplicates and for this reason it’s better to declare actions as constants like we did above.
Let’s create weather.reducers.js file.
In this reducer file we have import the weather constants as types.
I have destructured the action parameter that’s why you can see type and payload instead action.
We have a switch case inside the weather function and reducer will return the states according to the current action type.
So like this we can have many reducers for many states in the same application. so we have to combine all of these reducers in a one place.
For that we need root.reducer.js file.
This is the root reducer file. As you can see we will import our each and every reducer file here and will pass them to combineReducers. So we can have all of those reducers in one state.
Redux Selectors
What? Selectors? Why do we need selectors?
npm i reselect
we need to install reselect package in order to create selectors.
A selector looks like this. A selector is a small function you write that can take the entire Redux state, and pick out a value from it.
Let’s say you need to get the isLoading state inside your component, if you don’t have a selector you have to get the entire state for that. But with selectors, you can pick what you want.
Now we have created actions, reducers, constants and selectors. Here I’m not going to show about creating a react component, instead I’m going to show how to connect redux into a react component.
Before that, Let’s have a look on Redux — Saga
Redux — Saga
Redux-Saga use generators instead of normal functions. Because of that we are able to test, write and read async calls easily.
STOP! ✋🏻 thinking about generators? read more about generators 👈
Let’s Implement the middleware
First we need to install the package.
npm install redux-saga
Redux Saga has two main function types.
- A worker function
- A watcher function
Watcher is a generator function watching for every action that we are executing. In response to that action watcher will call a worker function. Normally we keep one Watcher function and we can have multiple worker functions in a single saga.
In here getCurrentWeatherStart is a worker function. and weatherSaga is a watcher function
The worker function will make the API call from redux-saga/effects. after the data loaded we will dispatch another action from our saga using put from redux-saga/effects.
Now we need root. (not Joe Root 🤭) we need a root saga like we create root reducer earlier.
So here is the root.sagas.js file. In here we use all from redux-saga/effects. Then we import all the sagas in our application to root saga and call them inside rootSaga generator function.
Now we are going to modify our store. so open the index.js file inside the store folder.
First import applyMiddleware from redux.
Then install redux-devtools-extention package.
npm i redux-devtools-extension
Then import createSagaMiddleware from redux-saga and then do the modification like above.
Connect component with redux.
Open your component and import connect from react-redux.
Then the action which you want to dispatch from the action file. and then the all the selectors that you need.
In the end of the component file, you have to change above to following code.
mapStateToProps
“As the first argument passed in to connect, mapStateToProps is used for selecting the part of the data from the store that the connected component needs. It’s frequently referred to as just mapState for short.” — https://react-redux.js.org/
- It is called every time the store state changes.
- It receives the entire store state, and should return an object of data this component needs.
mapDispatchToProps
“ As the second argument passed in to connect, mapDispatchToProps is used for dispatching actions to the store. dispatch is a function of the Redux store. You call store.dispatch to dispatch an action. This is the only way to trigger a state change. “ — https://react-redux.js.org/
Conclusion
In this article I have not mentioned the all the steps which you need to make a full app. I just wanted to clarify the concept using few examples. But if you need you can see the full code from my Github. source code