We know that while building an application, there are multiple components involved. Through React, these components can be combined into one use and can communicate with each other through data transfer. Today, we will talk about using React with Redux.
But the question arises, how to keep a record or track the data to manage the components present in the state object. Core Concept Of React does not have such a feature or a management tool to do so. Therefore, Redux has come into the picture.
Top Companies offering React Development Services highly prefer Redux as it makes it easy for the developers to fix the state management problem and run the application smoothly. Redux is like a complimentary library to React, which provides the feature which ‘React’ does not have in it.
Redux helps to store the state and actions that are needed to be stored for future reference and management. React, and Redux are the two things that go hand in hand. Redux with React has been a great combination.
What is Redux?
Redux is nothing but a JavaScript library used to manage the state of the application and build a user interface. Redux is generally used with the React library or many other libraries to provide better support to applications with a satisfactory user experience.
The interesting thing about Redux is that it is lightweight and can be accessed very easily. Each state can be accessed by each component, which is needed for the time being.
So, let’s now find out more about using Redux with React. Also, you should Learn React and Redux.
If you are looking for adept developers, hire offshore developer to build effective solutions by using React with Redux.
Building Blocks of Redux
There are three factors that Redux is made of. These building blocks sums up Redux’s whole definition and provides answers to questions like What is React Redux, what does it do, and how is it necessary?
These factors are:
Action: Actions are simple JavaScript objects that describe the course of action concerning what happened to the application’s state without specifying how the application state changed.
This Block of Redux helps to transfer the new and current data to the stores and outline the event that took place in the application.
With Action, if you are looking to update the state of your store, all you need to do is send them to your store, ultimately dispatching them. Reducers handle other things.
One quick reminder is that there should be a type field in Every action object because this field helps to depict the course of action that is being dispatched, and that too should usually be a constant that you export from a file. The rest is optional such as a payload key containing data.
An Action looks like the one below:
{ actionType: "UPDATE_TITLE", payload: "This is a new title." }
One more noticeable thing is Action Creators. They are generally the functions that generate and return the simple JavaScript objects and are used to install dynamic data into action.
Reducer: Reducers are solid functions that present how the application state changes. As soon as the action dispatches to the store, the Reducer starts updating the course of action being passed.
To be more precise, as soon as redux dispatches the action to Reducer, the Reducer recalculates the new application state or maybe a part of it.
It is basically like a reduce function in JavaScript in which a single value is calculated from multiple values after a callback function has been carried out.
The two arguments related to Reduce function are:
- The state of the previous app
- The dispatched action new app state of return. (previousState, action) => newState
When it comes to applying it, the Reducers get very complex.
Therefore to simplify this, all you have to do is put them into multiple and simple reducers, which you can later combine with a Redux helper function called combineReducers.
The main Reducer is conventionally called Root Reducer.
Store: A Store is an object that holds the functions and state of its application. It is like a nest where you can store any data that you want. This object also helps to bring the Reducer and Action together.
Although it may be possible to create multiple stores theoretically, Redux does not follow this pattern. And therefore, it is advisable to create only one store for an application.
createStore(rootReducer);
This createStore method is used for creating the application’s store. It takes into account three things: rootReducer, initialState, and Redux middleware constant (the latter two being optional).
Understanding Data Flow in Redux
Data Flow in Redux encompasses all its building blocks along with the connection which combines them together.
To have a clearer perspective, let us assume that an event has been introduced by the user inducing a change in the original state.
The next series of happenings that will take place are as follows:
- First, the handler function will dispatch an action to the store with the store.dispatch() method.
- The Reducer will get the dispatched action passed on to it by Redux
- The store will then save the new state returned by the Reducer
- As the subscription to the store is there, the function will be called out updating the User interface.
Guiding Principles of Redux
Single Source of Truth
Redux operates with only one store for its application state.
This makes the coding easy to build several apps because the state from the server gets equalised without putting efforts on extra coding.
A single source also makes things easier such as debugging an application, faster development process etc.
Some, by having single storage, some of the functions which were earlier difficult to implement, become easy to implement.
State is Read-Only
It implies that the state cannot be changed or modified directly. The one and the only method to implement a state change is by dispatching an action that means describing what happened.
This will make sure that the network callbacks and views do not write directly to the state. And the change should take place only by emitting an action.
The store object itself has a small API with only four methods:
- dispatch(action)
- subscribe(listener)
- getState()
- replaceReducer(nextReducer)
So, as you can see, there’s no method for setting state.
Therefore, dispatching an action is the only way for the application code to express a state change:
var action = { type: 'ADD_USER', user: {name: 'Dan'} };
Changes are made with Pure Functions
As discussed earlier, direct changes are not possible in Redux. Therefore how to initiate the state change? The answer to this question is through Reducers.
Reducers are the function which describes the action and handles the dispatching process that has initiated from the action to modify the state.
The Reducer can only modify the state by returning a new state for Redux with React.
// Reducer Function var someReducer = function(state, action) { ... return state; }
The Need For State Management
While you start using React, you won’t find any need for separate software, especially for state management.
But as soon as you go a little further while building your application, you would realise the need for state management which React won’t provide you, and at that moment, Redux will come into the picture.
To understand it more clearly, let’s look at the chart shown below:
The above diagram shows the flow of data in the form of props from the parent component which is the source to the two different components which has been marked A and B.
This shows how the data flows through React that can be categorised as unidirectional data flow. Now here you’ll find no issue in the same management, and you’ll think that React is sufficient to build and manage the application.
But the next diagram will specify the real issue that you might face during the process.
From the above diagram, you might now process things more clearly. Here the data (props) flows through parent component to Component A & B and then to component C & D. While the need for the props was only in component C & D.
To be more precise, if any component needs some data, then it can only be transferred through React with the nearest common ancestor between the source (parent component) to the place needed (Component C&D). The data will travel through several components, even though it isn’t needed there.
You will now understand that transferring data like this may raise a lot of issues because each component in the flowchart is bounded by a structure that makes them so tight that it cannot move freely.
It will ultimately lead to time wastage and above that, the task will become so redundant and rigid that it will become irritating at some point.
In every state update, you will have to modify the codes of those components where the unnecessary data flow has taken place.
It will ultimately reduce the performance of the application which you have to build with a lot of precision.
Now here is when Redux comes into play. The problem of the unnecessary flow of data that React was unable to solve can be dealt through Redux.
It provides a centralised data store or a JavaScript object that helps you to nest all the data in a single place which can be accessed anywhere in the application wherever needed. The below diagram will help you visualise it.
With the help of Redux, you are not bound to pass on the data through multiple components which do not need the data.
The data can very well be accessed to those components where it is needed and through a single source which is Redux store.
Here the components that are involved are limited according to the requirement, and the coding process is also easy and less time-consuming which is ultimately enhancing the app’s performance.
Steps For React With Redux
Setting Up The App
For setting up Redux with React in the application, first create an application and for that create the files and folders needed as a source manually as shown below, and that is when you’re not on Linux.
mkdir -p redux-notes-app/{dist,src/{actions,reducers,store}} && cd redux-notes-app && touch {index.html,.babelrc,webpack.config.js,src/{actions/actions.js,reducers/reducers.js,store/store.js,main.js}}
If you are on Linux then the first step is pretty simple for you; you have to search through your project directory and paste it in your terminal. It will save your time and efforts.
Defining The App State
The initial stage starts with planning. It is one of the crucial exercises one has to follow if you are expecting a proper execution and a better result.
So, before jumping into writing the program, just pin down the list of events and the structure of the application. Interlink the data needed and visualize the final result of how the application would look like.
One of the samples for you to have a better understanding is shown below:-
{ notes: [ { title: 'Note 1 Title', content: 'Note 1 Content' }, { title: 'Note 2 Title', content: 'Note 2 Content' }, ... { title: 'Note N Title', content: 'Note N Content' } ] }
Knowing the Initial Code
Some initial codes are needed for the application. The following codes are stated below that you can insert it in your index.html file:
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Redux Notes</title> </head> <body> <h1>Redux Notes App</h1> <h3>Add a Note</h3> <form id="add-note"> Title: <br /> <input type="text" name="title"> <br /> Content: <br /> <textarea name="content" cols="30" rows="5"></textarea> <br /> <button type="submit">Add Note</button> </form> <hr /> <h3>All Notes</h3> <ul id="notes"> <li> <b>Title</b> <button data-id="5">x</button> <br /> <span>Note Content</span> </li> </ul> <script src="./dist/bundle.js"></script> </body> </html>
After that insert, this code in your src/main.js file:
stc/main.js
// ------ HTML references ------ let notesUList = document.getElementById('notes'); let addNoteForm = document.getElementById('add-note'); let addNoteTitle = addNoteForm['title']; let addNoteContent = addNoteForm['content']; // ------ Redux ------ function deleteNote(index) { // console.log(index); } function renderNotes() { setDeleteNoteButtonsEventListeners(); } // ------ Event Listeners ------ addNoteForm.addEventListener('submit', (e) => { e.preventDefault(); // console.log('Title:', addNoteTitle.value, 'Content:', addNoteContent.value); }); function setDeleteNoteButtonsEventListeners() { let buttons = document.querySelectorAll('ul#notes li button'); for(let button of buttons) { button.addEventListener('click', () => { deleteNote(button.dataset.id); }); } } // ------ Render the initial Notes ------ renderNotes();
A simple form with title and content field. This will be used for adding notes.
Creating Actions
Now it’s time to create some actions by adding a note. Inside actions.js, add initial code:
src/actions/actions.js export const ADD_NOTE = 'ADD_NOTE'; export function addNote(title, content) { return { type: ADD_NOTE, title: title, content: content }; }
To make you understand the property. The step by step procedure is as follows:-
- A constant ADD_NOTE is exported at first for future reference as there will be a need in several places, and having written it everywhere, later on, may look bad.
- Next, export the function addNote as this function is an action creator, which means that its job is only to return a plain object.
Creating Reducer
Next step is to create reducer for defining the action before creating the store for dispatching actions
import {ADD_NOTE} from '../actions/actions'; const initialState = { notes: [] }; function rootReducer(state = initialState, action) { switch(action.type) { case ADD_NOTE: return { notes: [ ...state.notes, { title: action.title, content: action.content } ] }; default: return state; }; } export default rootReducer;
As we discussed earlier, there are two parameters that are received by reducer, the first is the previous state and the other one is the action being dispatched for state change.
Here in the above program, we declared the initial state object that actually is the state object of the whole application as this is the only reducer in the application.
Remember never to make any changes to the state directly, the reducers should always be immutable. Now here, check the action type and on the basis of that value, return the new state.
Creating App Store
Now it’s time to create the store for the application.
import { createStore } from 'redux'; import rootReducer from '../reducers/reducers'; export default createStore(rootReducer);
Redux has the function of createstore through which you can easily create the application store easily without any difficulty.
It works with three parameters, the other two are optional but for now the mandatory parameter which accepts the root reducer of the application is in use.
Next step is to import the store in the main.js file and dispatch some actions to see what your state looks before and after adding a few notes.
import store from './store/store'; import { addNote } from './actions/actions'; // We use store.getState() to get our app state from the store console.log('Before:', store.getState()); store.dispatch(addNote('One', 'One content')); store.dispatch(addNote('Two', 'Two content')); store.dispatch(addNote('Three', 'Three content')); console.log('After:', store.getState());
Adding Notes
For adding a new note, just add 3 new lines in the form event handler:
addNoteForm.addEventListener('submit', (e) => { e.preventDefault(); let title = addNoteTitle.value; let content = addNoteContent.value; store.dispatch(addNote(title, content)); });
This is a simple dispatching of action to the store.
Rendering Notes
Whenever you want to dispatch an action to the store, the subscribe function can be used to make changes in the state. Use the subscription to render notes for making changes and updating the application.
function renderNotes() { let notes = store.getState().notes; notesUList.innerHTML = ''; notes.map((note, index) => { let noteItem = ` <li> <b>${ note.title }</b> <button data-id="${ index }">x</button> <br /> <span>${ note.content }</span> </li> `; notesUList.innerHTML += noteItem; }); setDeleteNoteButtonsEventListeners(); }
Now it’s time to subscribe to the store:-
store.subscribe(() => { renderNotes(); });
Now you are ready to return to the original page and try adding the new notes.
Unsubscribing From The Store
If you are looking to unsubscribe from the store, then you can use the store.subscribe function that will return the function calling for unsubscribing from the store.
const unsubscribe = store.subscribe(() => { renderNotes(); }); unsubscribe(); // Adding new notes won’t trigger a change in the UI now
Initializing State
The Next step is to initialize the state change so that updation could take place.
But before that, let’s have a glance at the properties of reducer:-
- You will be surprised to know that each reducer has its own state and that can be distinct from the state of application especially when there are more than one reducer.
- Since the example only specifies one reducer, therefore the state of application as well as state of reducer are common.
When initialization takes place in the application through redux, an action is sent which will not satisfy any cases in the switch statement.
In fact, if we log the action type it in the console, this is what we’ll get the first time we visit the page:
Since we haven’t supplied any initial state of the application therefore it defaults back to the initial state object.
- As in the example there is only one reducer but when you’ll be building an application, there might be multiple reducers and for that you will have to write for all the reducers.
Redux has a function called combineReducers that allows you to bind all the reducers and transfer them to the createStore function to make it a single reducer.
Now comes the interesting part:
In the combineReducers function, every reducer handles its own state of the application as soon as there is a passing of an object to the reducer. Redux then makes the state of the application from all the keys and values of the object.
First, create a new file notesReducer.js inside the src/reducers directory where the movement of the code will take place from the src/reducers/reducers.js file and mold it a bit:
import { ADD_NOTE, REMOVE_NOTE } from '../actions/actions'; function notesReducer(notes = [], action) { switch(action.type) { case ADD_NOTE: return [ ...notes, { title: action.title, content: action.content } ]; case REMOVE_NOTE: return notes.filter((note, index) => index != action.id); default: return notes; }; } export default notesReducer;
Note: There is a change in the first parameter from state to notes.
As the reducer only will handle notes, it will always return an array. Apart from that, no other state will be transferred to the reducer.
Next, add another reducer for managing filtering notes according to visibility.Let’s call it visibilityFilter.
Hint: For utilizing the visibility filter, you can induce a tag field in the note object. Then, you can add on some more tags and actions for making changes in the tags of a single note.
And at the end, just add a select element to show only the tagged notes.
import { SHOW_ALL } from '../actions/actions'; function visibilityFilter(visibility = SHOW_ALL, action) { switch(action.type) { case SHOW_ALL: return SHOW_ALL; default: return visibility; }; } export default visibilityFilter;
In the actions.js file, insert the following content:
export const SHOW_ALL = 'SHOW_ALL'; export function showAll() { return { type: SHOW_ALL }; }
Now it’s time to join the reducer to the reducers.js file.
import notesReducer from './notesReducer'; import visibilityFilter from './visibilityFilter'; import { combineReducers } from 'redux'; const reducers = combineReducers({ notes: notesReducer, visibility: visibilityFilter }); export default reducers;
Finally, the codes for the store.js file is:
import { createStore } from 'redux'; import reducers from '../reducers/reducers'; export default createStore(reducers);
Now if you will log the state application in your main.js file, this is what you’ll get:
Connecting App With Redux DevTools
Redux Dev Tool is one of the most effective tools which will help connect components in the app that will ultimately increase the performance of the application.
Now, add the below line as the third parameter of the createStore function:
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
This is how your store.js file should look like now:
import { createStore } from 'redux'; import reducers from '../reducers/reducers'; let initialState = { notes: [ { title: 'You are awesome', content: 'No, wait, I meant legendary!' }, { title: 'Ooops', content: 'I was talking to myself' } ], visibility: 'AWESOME_TAG' }; export default createStore( reducers, initialState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() );
Utilizing The react-redux Package
To bind the react-redux component, a package is also available: react-redux which will ease the application building process.
In order to install React-Redux package, all you have to do is open up a terminal window in your React project’s directory and execute the following command:
npm i redux react-redux
Example Of React With Redux
To have a better understanding of Redux and its performance, here is a simple React Redux example to illustrate it.
Step 1: Install create-react-app
The initial step would be to install react-app. And if done already, then go to the next step.
1npm install -g create-react-app
Step 2: Create An App
The next is to create the application:
1create-react-app react-redux-counter
The process might take some time. But as soon as there’s a completion, enter your project directory:
1cd react-redux-counter
Step 3: Installing react-redux Package
As the support of both will do the work react as well as redux; therefore, the next step would be to install React-Redux package.
1npm install redux react-redux –save
Step 4: Creating Files & Directories
After the installation, create all the files and folders necessary for the application just to simplify the task and avoid wastage of time:
1mkdir -p src/store && touch src/store/action-types.js && touch src/store/action-creators.js && touch src/store/reducers.js 2mkdir -p src/components && touch src/components/Counter.js && touch src/components/counter.css
Step 5: Describing the Actions
The next step is to describe the actions for the application In src/store/action-types.js
1export const TYPE_INCREMENT = 'INCREMENT' 2export const TYPE_DECREMENT = 'DECREMENT'
The counter can have incrementation or decrementation.
Step 6: Defining Action Creators
In src/store/action-creators.js, Describe the action creators. And you must know that actions are simple JavaScript the course of happenings in an application.
import { TYPE_INCREMENT, TYPE_DECREMENT } from ‘./action-types’
export const increment = () => ({ type: TYPE_INCREMENT, }) export const decrement = () => ({ type: TYPE_DECREMENT, })
In the above functions, there is return action having a type of property that is mandatory.
The actions will dispatch to the reducers, which will answer the question about how it will happen. There is additional data also that action will carry, but that is optional and not relevant for this example.
Step 7: Defining App’s State & Reducer
In src/store/reducers.js, you will define your app’s initial state along with your reducer.
import { TYPE_INCREMENT, TYPE_DECREMENT } from ‘./action-types’
const initialState = { counter: 0, } export const counterReducer = (state = initialState, action) => { switch (action.type) { case TYPE_INCREMENT: return { ...state, counter: state.counter + 1, } case TYPE_DECREMENT: return { ...state, counter: state.counter - 1, } default: return state } }
In a React Redux application, whenever there is an action, redux calls out for Reducer. Since the example has one Reducer that does not mean that other action-type cannot be done.
Based on the action type, you do three different things here:
- A new state with increased counter
- New state with decreased counter
- State unchanged
Step 8: Combine React with Redux
Now the next step is to bind React and Redux together. And for that, go to src/index.js and replace its content with this piece of code:
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import { counterReducer } from './store/reducers'; import App from './App'; const store = createStore( counterReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), ); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root'), );
Step 9: Creating Counter Component
Now it’s time to create actual Counter component in src/components/Counter.js:
import React from 'react' import { connect } from 'react-redux' import { increment, decrement } from '../store/action-creators' import './counter.css' export const CounterComponent = ({ counter, handleIncrement, handleDecrement }) => ( <div> <p className="counter">Counter: {counter}</p> <button className="btn btn-increment" onClick={handleIncrement}>+</button> <button className="btn btn-decrement" onClick={handleDecrement}>-</button> </div> )
CounterComponent is the component which shows the current counter and calls handleIncrement or handleDecrement on button clicks.
Take this stateless functional component and then use connect from react-redux to make the store available to it:
const mapStateToProps = ({ counter }) => ({ counter, }) const mapDispatchToProps = { handleIncrement: increment, handleDecrement: decrement, } export const Counter = connect( mapStateToProps, mapDispatchToProps, )(CounterComponent)
Step 10: Adding Basic Styles
Add basic styles in src/components/counter.css:
.btn, .counter { font-size: 1.5rem; } .btn { cursor: pointer; padding: 0 25px; }
And then replace the contents of src/App.js to render our component:
import React, { Component } from 'react'; import { Counter } from './components/Counter' class App extends Component { render() { return ( <div className="App"> <Counter /> </div> ); } } export default App;
Now run npm start in your terminal. And you are good to go. You can also run the same app without redux to see the difference. All you must do is clone the repo and run git checkout without-redux.
Exploring the React Developer Tools
When exploring the React Developer tool, there is one of the tools which is very popular, and that is Redux DevTools.
After installing it, the below picture will be seen.
In the left-hand side of the tool, there is a list of actions.
By clicking on any action, you can come across the point when the dispatch happened, or you can skip the action and go further.
The right-hand side panel shows some options that allow you to have a look at the data as well as the action type that it carries.
The slider at the bottom also allows you to time travel through the dispatched actions. You can go back and forth and replay the actions that changed the original state.
Read also: React Native Database – How To Choose Right Database For React Native App?
There Always A Need for Redux?
There might be times when you may not need Redux mainly because of the simplicity of the project that you are handling.
In that case, the management of data becomes easy and therefore combining React with Redux may not be necessary.
If the component tree of your project does not involve multiple branches and the transferring of data is easy and quick without many efforts, then don’t overlap React with Redux and let it go smoothly.
But if the component tree is complex then it is advisable to go with Redux- React package because it will help to share the state between components easily which are wide apart from each other. And therefore, setting up React with Redux is quite efficient. You can get the benefits when you hire dedicated ReactJS developers who is efficient enough to give these benefits.
Conclusion
Combining React with Redux is the best option when it comes to building a complicated application which has various components and managing and storing them requires a great deal.
And therefore, for that, it is advisable to have a react-redux package for it makes your work much easier and improves the performance of the application as well.
We hope you had a great time reading this article and it proves to be of great value for any React Native Application Development Company in the near future. Thank You.!
-
How Does Redux Work With React?
Ready is basically a state management tool. It is mostly preferred with React, but also viable for other JavaScript frameworks. With Redux, you have a state of the app kept in a store and each component accesses it as per needs.
-
How To Use A React Router With Redux?
There are some steps that you need to follow in order to use a React Router with Redux. They are as follows:
Installing React Router
Configuring The URL
Connecting React Router with Redux app
Navigate The React Router -
What Is The Difference Between Redux and React Redux?
Redux is a state container that comprises all info of the app, while React Redux is a state of the entire application which comprises components.
-
Is Redux Still Used In 2024?
The simple answer to this question would be YES. Redux is still utilized by many applications to manage complex states. However, it’s not compulsory.