React-Redux : Core Concepts You Need To Know
20 May, 2023
1
1
0
Contributors
A lot of terminologies in Redux make it seem like instead of simplifying things we are just increasing the complexity of an application. There’s so much to learn and it can be a bit overwhelming for anyone.
If you are looking for a single resource to understand all the basics, keeping everything else aside which is not important to learn then I am here to help you. In this blog I will try my best to help you understand all the basic concepts of Redux without making you confused with a bunch of terminologies.
So, what is Redux?
It is an open source state management JavaScript library for managing an application's state.
In a React application, most of the time you define the state in your application at the top-level component but as your application grows, all your state in a top-level component is no longer sufficient and it becomes difficult to manage all your state in the application. You may also have alot of data changing in your application over time. State management is a big concern in large applications and Redux solves this problem.
Data flow in a React-Redux Application
There are four fundamental concepts that govern the flow of data in React-Redux applications.
- Redux store : This is an object that holds the application state, it consists of small state objects which are combined into one large object. Any component in the application can easily access this state(store) by hooking up to it through the connect method.
- Action creators: These are functions that return actions(objects), they are invoked when the user interacts with the application through its UI or at certain points in a component’s lifecycle.
- Actions: These are simple objects which conventionally have two properties- type and payload. The type property is usually a string that specifies the action and the payload is an optional property that contains some data that is required to perform any particular task. The main function of actions is to send data from the application to the Redux store.
- Reducers: are pure functions that update the state of the application in response to actions. They take the previous state and an action as the input and return a modified version of the state. Since the state is immutable, a reducer always returns a new state which is an updated version of the previous state.
React-Redux Application Flow Explained
The flow of data in a React-Redux application begins at the component level when the user interacts with the application UI. This interaction leads to the action creators dispatching an action.
When an action is dispatched, it is received by the root reducer of the application and is passed on to all the reducers. Thus, it becomes the reducer’s task to determine if it needs to update the state based on the dispatched action.
This is checked using a simple switch statement to filter out the required actions. Each reducer in the application accepts the dispatched action and if the type of the dispatched action matched, it returns a newly updated state.
It is essential to note that the new state never actually changes in redux. Instead, the reducer always generates a new state which is a copy of the old state but with some modifications.
The store then informs the component about the new state which in turn retrieves the updated state and re-renders the component.
Another important observation here is that the flow of data in a React-Redux application is unidirectional i.e. It only goes in one direction.
What are the advantages of using Redux with ReactJS?
- Centralized state management system i.e. store - React state is stored locally within a component and to share this state with other components in the application, props are passed to child components, or callbacks are used for parent components. Redux state on the other hand is stored globally in the store. All components of the entire application can easily access the data directly. This centralizes all data and makes it very easy for a component to get te state it requires.
- Performance optimizations: By default whenever a component is updated, React re-renders all the components inside that part of the component tree. In cases when the data for a given component hasn’t changed these re-renders are wasted. Redux store helps in improving performance by skipping such unnecessary re-renders and ensuring that any given component re-renders only when its data has actually changed.
- Pure reducer functions: A pure function is defined as any function that doesn’t alter input data, doesn't depend on the external state and can consistently provide the same output for the same input. Redux depends on such pure functions and takes a given state(object) and passes it to a each reducer in a loop. In case of any data changes, a new object is returned from the reducer(re-rendering takes place). However, the old object is returned if there are no changes(no re-rendering).
- Storing long-term data -Since data stored in redux persists until a page refresh, it is widely used to store long-term data that is required while the user navigates the application, such as data loaded from an API, data submitted through a form etc. On the other hand React is suitable for storing short-term data that is likely to change quickly.
- Time-travel debugging: Redux makes debugging an application an easy process since it represents the entire state of an application at any given point in time.
Three core principles of Redux
Consider the real-life scenario of banks. You want to withdraw or deposit some cash from your bank account. You go to the bank branch with intended action is to either DEPOSIT_MONEY or WITHDRAW_MONEY.
You are aware that there is a process that you need to follow to withdraw your money. When you talk to your cashier he takes your details and confirms if they are true and hands over the cash to you.
1.Consider the Redux Store as a back vault and the state of your application is like money.The entire user interface of your application is a function of your state. Just like your money is safe in the bank vault, the state of your application is safe in the Redux store. This leads to the first principle of Redux:
Single source of truth: The state of your whole application is stored in an object within a single store.
In Redux it is advisable to store the application state in a single object managed by the Redux store.
2.Just like you follow a process to withdraw money from your bank, Redux also follow a process to change/update the state of your application. This leads to the second principle of Redux:
State is read only.
The only way to change the state is to emit an action an object describing what happened. In Redux your action WITHDRAW_MONEY will be represented by an object and it looks something like:
{
type: "WITHDRAW_MONEY",
amount:"$10,000"
}
The above object in the Redux application that has a type field describing the action you want to perform. So whenever you need to change/update the state of your Redux application, you need to dispatch an action.
3.Consider your cashier in the bank as the reducer in your application. To WITHDRAW_MONEY from your bank vault(store), you need to convey your intention/action to the cashier first who will then communicate your intended action to the bank that holds all the bank’s money.
A similar thing happens in Redux, to update the state of your application you need to convey your action to the reducer. Now the reducer will take your action, perform its job and ensure you get your money. Your reducer will always returns your new state. Sending off the action to the reducer is dispatching an action.
This leads to the third principle of Redux:
To specify how the state tree is transformed by actions, you write pure reducers.
To deepen your fundamental concepts in Redux let’s take an example of a simple React application. If you are familiar with React you shouldn’t have a problem in understanding the structure of the React application.
It’s totally up to you on how you want structure your pojrect but a common approach in Redux is to create a separate folder/directory for the major components such as reducer, ations and store. In each of these folders create an index.js file that will be the entry point for each of the Redux components.
Whenever we to store anywhere in our app we can import the store file y mentioning the path import store from “./store”.
The main concern here is to refactor the App component and use Redux in it. Redux is the state manager for our application so we need to take away the state object we want to be managed by Redux.
We need to first install Redux by running npm install -save redux
Create Redux Store
Some APIs are provided by the Redux library to create the store facility.
import {createStore] from "redux"
const store = createStore();
We have imported the createStore
factory from Redux and then we have invoked the createStore()
function to create the store.
Define the store and reducer relationship
The store(bank vault) and the reducer(cashier) communicate with each other and they are always in sync. We pass the reducer as an argument in createStore()
function
const store = createStore(reducer);
The reducer
Reducer is a function in Redux that takes two parameters. One is the state of the app and the other is action. The path for the reducer function will be src/reducer/index.js. Right now we will pass store as an argument.
In redux if you don’t perform any action and you don't pass the action as an argument in the reducer then the state will remain the same and the reducer will return the same state as the new state.
If you dispatch any kind of action such as withdrawing or depositing any amount of money in your account this is known as the initialState
and will represent this initialState
as a second argument passed into the createStore
.
const store = createStore(reducer, initialState);
So the initialState
is like an initial action point and if you don’t perform any action, this initialState
will be returned as the state of the application.
Redux Action
In a pure React application, we use setState
method to update the state in your application but here we need to let our action known to the reducer to update the state in your application.
We do this by describing the action using a plain JavaScript object and this object must have a type field to describe the intent of the action. Many times in our application we also need to add some additional information. The standard approach in Redux is using the payload field. We put all the required data/information in the payload object that describes the action and it looks like something below.
{
type:"withdraw_money",
payload:{
amount:"$3000"
}}
Handling responses to actions in the reducer
We’ve established that a reducer takes two arguments in order to update the application one is the state and the other is action.
Now to handle the action passed into the reducer we typically use switch statements in Redux
function reducer (state, action) {
switch (action.type) {
case "withdraw_money":
//do something
break;
case "deposit-money":
//do something
break;
default:
return state;
}
}
In the above example we have taken two actions. One is withdraw_money and the other deposit_money. Based on your applications requiremnets you can define as many actions as you want but every action flows through the reducer.
Actions pass through the reducer which differenciates each of them by switching over the action.type. This is how each action can be handled separately in the reducer without any inconvenience.
To update the state of an application we need to dispatch an action and the state of the application will be updated.Once the state is updated we expect our application to re-render the new state values.