How to use Redux in ReactJS with Class & Function Component (Explained with Example)

    This article is about the basic concepts of redux & react-redux as simple as possible. Here we are going to learn how to use redux with react class as well as a functional component.
    Let’s learn this through theory & examples by building simple react applications. We will be building 2 applications React- redux for class Component & React-redux for a functional component.

Live demo project for react-redux class and function based components:

1)Basic concepts of Redux:

     Redux is a state management library for javascript applications. we can use Redux with React, angular vue.js & vanilla js because Redux is a state management library it doesn’t care about what library we use.
     Let’s understand what is state: a state is a global object which contains information that you use for various purposes in the app.

    For Example:1)suppose we have async data fetching functionality that uses some API to fetch data and display it in the app. Now our aim is to store the data in some object (here its state) & also show a loading indicator while the app is busy fetching data. We can do this using some boolean variable in the state object which will change according to the state of data that API has fetched.

       2)another example is an e-commerce website build with component-based architecture, such applications need to access the same data in multiple components. In such a case if one component modifies the data it should reflect that change in all other components as well. for all these requirements we create a central store using redux. redux will manage the state of the store for our application.


2)Installation of Redux & React-Redux:

    To use redux with our react application we need to follow some initial installation processes as below.

    To install redux we first need to set up the node runtime environment.

1.Visit https://nodejs.org/ and install the package file.
2.Run the installer, follow the instructions and accept the license agreement.
3.Restart your device to run it.
4.Check Node & npm installation along with versions use npm -v & node -v

1.Install Redux:
To install redux use the below command:

 npm install --save redux
// yarn add redux  

2.Install react redux:
To use react with redux we need to install react-redux:

 npm install --save react-redux
 //yarn add react-redux


3) Redux Pattern:

     Everytime you use redux you need to keep in mind these two patterns,

1)Single source of truth: Single source of truth means we have only one place (here store) where we store the only state of our app. which means one application has only one store & only one state. But we are free to use as many states as possible because using a single central state will cause confusion when the app grows considerably larger.

2)Immutability: Immutability is quite a popular term, most of the frameworks & libraries use it. In our case, immutability means that we don’t change the state of the store directly instead we will create a new state object & re-calculate the new state & update it with the new state object.

    Let’s get started with redux: Redux has 3 main parts as shown below,
1)Reducer
2)Store
3)Actions


1)Reducers:

”reducers are the pure functions that update the state of redux store based on the action” 

    To update the store we should create a function that takes 2 arguments the store & action object and returns the update store based on the action.Now lets create a reducer as a pure function with 2 parameters i.e state & an action. The job of this reducer is to return new state based on the action.

    We are using an example of a store, the store has multiple items & each item is having its id, name & item description.

var itemId = 5;
function reducer(state=[] ,action){
switch(action.type){
        case "itemAdded":
                return  [
                ...state,
                {
                id:++itemId,
                name:action.payload.name,
                description:action.payload.describtion,
                }
                ];
                break;
       case "itemRemoved":
                return state.filter(item=>item.id!== action.payload.id);
                break;
          default:
                return state;
}
}
export default reducer;

        …state – it’s a spread operator to copy the state of the store, the alternative for this is we have immutability library or immer library.

        initially, the state is undefined, redux call the reducer and pass undefined as the value of the state. In this case, we want to return the initial state, not the undefined state hence we used the default argument as an empty array.


2)Store:

        In redux, we store our application state inside a single javascript object called the store. this store object is the source for our application state & it is accessible by all parts of UI. What we can have inside the store is totally up to us we can have anything like arrays, objects, numbers, booleans, essentially anything that represents the data our application needs to function.

Rules for working with store:

1.we must not directly change or modify the state in the redux store because the redux is built on top of functional programming principles.
        Note: In functional programming we can’t mutate the state, hence we cant modify the store object directly.

2.To modify the state of the redux store we must create a plain action that describes “what to do” & then dispatch this action to the store.

3.when action is dispatched then redux store runs the root reducer, root reducer calculates the new state based on the old state and action.

4.Finally store notifies the subscriber using the subscriber function that state is updated & now UI can be updated with new data.

To create a store we need to import createStore function from redux.

        import { createStore } from ‘redux’;

Now, we need to pass the reducer to createStore,

        import reducer from ‘./reducer’;

        createStore(reducer);

        we need to pass the reference of the reducer to createStore.CreateStore is a higher-order function that takes a function reference as an argument.

        The final code for creating store is as follows:

import {createStore} from 'redux';
import reducer from './reducer';

let initialState=[
      { 
         id:1,
         name:'item1',
         description:'item1 with id 1'
      },
      { 
         id:2,
         name:'item2',
         description:'item2 with id 2'
      },{ 
         id:3,
         name:'item3',
         description:'item3 with id 3'
      },{ 
         id:4,
         name:'item4',
         description:'item4 with id 4'
      }
];

const store= createStore(reducer,initialState);

store.subscribe(()=>{
   console.log("store",store);
   console.log("store state",store.getState());
});
export default store;

Browser Console:

    store has below properties:

1.getState
2.dispatch
3.subscribe
4.replaceReduer
5.Symbol(Observable)

1.getState():

    In redux to access the state of the store we are avaialble with getState() method.

Syntax:

  Store.getState();

    well, we only have getState() not the setState, this is the fundamental principle in redux. To change the state of the store we have to dispatch the action. With this architecture we are essentially sending every action through the same entry point i.e dispatch this is the beauty of redux. To check the state of store use

        console.log(store.getState());


2.Disptach the Actions:

 store.dispatch({
 //action Object
 });


3.subscribe to the store:
        subscribe is a function that gets called every time when the state of the store gets change. UI components should subscribe to the store so that they get notified when the state of the store changes.

ex:

   store.subscribe(()=>{
    console.log("store changed!!!");
   });

use of subscribe:
        If your building ur application with vanilla js or jquery then this is the place where we are gonna work with DOM elements to refresh the view. If you are building the application with react then we will re-render.

 store.subscribe(()=>{ console.log(store.getState());})

unsubscribe:
1.this subscribe function returns a function for unsubscribing from the store.
2.subscription creates a memory leak when the user gets navigated to other parts of UI where he/she doesn’t use the store subscription.

     Hence need to unsubscribe from the store.

const unsubscribe = store.subscribe(()=>{console.log(store.getState());});

unsubscribe();
        if we unsubscribe from the store then we don’t get notified by the store.subscribe.


3)Action:

       An action is just a plain javascript object that describes what just happened. based on the type of action reducer just comes to know what properties it should update.

{
   type:"userAdded",
   payload:{    
      //data to be stored in redux state
   }
} 


4)common setup for redux with react for class & function component:

React-redux Provider:

        React-redux package provides a react component called Provider, which makes our application store available throughout the application. This can be done by surrounding our App Component within the Provider component in our index.js file & passing the store object as a Props to Provider Component.

        Below is the demo for using Provider to wrap App component.

 import { Provider } from 'react-redux';
 import store from './redux/store/store';
  
 ReactDOM.render(
   <Provider store={store}>
     <App />
   </Provider>,
   document.getElementById('root')
 ); 


5)React-Redux with Class Component:

          There are two approaches we can use to create a react app one is using class-based components & the other is function-based components.

Connect API:

          For a class-based component, we use Connect API from the redux package by which we can connect our component to the redux store. i.e we can make our component interact with our redux store.
          Connect is a higher-order function. It returns a function to which we provide the class name of our component. connect API has 2 more optional parameters:mapStateToProps & mapDispatchToProps.Let’s discuss these 2 parameters first.


1)mapStateToProps:

        mapStateToProps is the first parameter to connect API of Redux store. It subscribes to the store by mapping store values in our component properties i.e mapping store values to props.

1)mapStateToProps is called every time our application state or props change.

2)mapStateToProps is a function that takes the entire state of our Redux store as its first parameter and returns an object of data that our component needs from the store.

Syntax:

        function mapStateToProps(state, ownProps?)

        mapStateToProps second argument called ownProps if your component needs the data from its own props to retrieve data from the store. This argument will contain all of the props given to the wrapper component that was generated by connect.


2)mapDispatchToProps:

        connect API has a second parameter called mapDispatchToProps,mapDispatchToProps is used to dispatch the actions to store. dispatch is the only way to trigger the state change. with react-redux, our component never going to access the store object directly, instead connect does it for you.
React-redux provides two ways to let your component dispatch the action:

1) by default a connected component receives props.dispatch.

   ex:

class MyComponent extends React.Component() {
   return (
     <div>
       <button onClick={() => this.props.dispatch({ /*action Object*/})}></button>
     </div>
   )
 }
 export default connect()(MyComponent) 

 2) connect can accept an argument called mapDispatchToProps,

         which lets you create functions that dispatch when called, and pass those functions as props to your component.mapDispatchToProps function will be called with dispatch as the first argument. this function normally returns an object with new functions that call dispatch() inside the object.
        If your mapDispatchToProps function is declared as taking two parameters, it will be called with dispatch as the first parameter and the props passed to the connected component as the second parameter and will be re-invoked whenever the connected component receives new props.

mapDispatchToProps returns the plain Object:

1)Each field in the object will become a separate prop for your own component, and this field should normally be a function that dispatches an action when called.

Let’s create a react app named myitemlist using Facebook’s Create React App tool.

npx create-react-app myitemlist

Now install the redux & react-redux package as mentioned above in Installation step

Now Let’s create class Component called ItemList inside the src folder in myitemlist app.

This ItemList component has 2 features:

  1. It displays all the items present in the redux store in right flexbox, these items can be deleted using the delete button beside each item.
  2. ItemList component has Add item feature which adds every item into the redux store.

Components:

        Our main Component is ITemList component .ItemList component has mainly two sections one for adding Item to redux store and second for displaying items from store.

import React from 'react'
import './ItemList.css';
import { connect } from 'react-redux';
class  ItemList extends React.Component {
    constructor(){
        super();
        this.state={
        }
        this.onInputchange = this.onInputchange.bind(this);
        this.deleteItem=this.deleteItem.bind(this);
        this.addItem=this.addItem.bind(this);
    }

 deleteItem(id){
     this.props.deleteMyItem(id);
     alert("item deleted with id:"+id);
    }
addItem(event){
    event.preventDefault();
    this.props.addMyItem(this.state.name,this.state.description);
        alert("item "+this.state.name+" is added to store!!!");
    this.setState({
      name:"",
      description:""
    });
}
onInputchange(event) {
    this.setState({
      [event.target.name]: event.target.value
    });
  }
    render(){
    return (
        <div className="container">
            <div className="container-flex">
               <div className="add-item">
                 <div className="item-heading">
                 <h2>Add Item</h2>
                 </div>
                 <form onSubmit={this.addItem}>
                 <div className="item-data">
                   <label>Item Name</label>
                   <input type="text" name="name" value={this.state.name} onChange={this.onInputchange} id="name" placeholder="Enter item name" required/>
                   <label>Item Description</label>
                   <textarea placeholder="Item Description" value={this.state.description} onChange={this.onInputchange} id="description" name="description" required></textarea>
                 </div>
                 <div className="item-button">
                 <input type="submit"/>
                 </div>
                 </form>
               </div>
               <div className="item-list">
                  <div  className="item-heading"> 
                  <h2>Item List</h2>
                  </div>
                  <div className="item-table">
                  <table>
                        <thead>
                        <tr>
                            <th>Item Id</th>
                            <th>Item Name</th>
                            <th>Item Description</th>
                            <th>Delete Item</th>
                        </tr>
                        </thead>
                        <tbody>
                            {this.props.items && this.props.items.map((item,index)=>{
                                return (
                                    <tr key={index}>
                                    <td>{item.id}</td>
                                    <td>{item.name}</td>
                                    <td>{item.description}</td>
                                     <td><button onClick={()=>this.deleteItem(item.id)}>Delete</button></td>
                                    </tr>
                                )}

                            )}
                        </tbody>
                    </table>
                    </div>
               </div>
            </div>
        </div>
    )}
}

const mapStateToProps = (state) => {
    return {
        items:state,
    };
}
const add = (name,description) => ({ type: 'itemAdded',payload:{
         name:name,
         description:description
}});
const delete1 = (id) => ({ type: 'itemRemoved',payload:{
id:id
} });

const mapDispatchToProps =  (dispatch) => {
  return {
    addMyItem:(name,description) => dispatch(add(name,description)),
    deleteMyItem: (id)=> dispatch(delete1(id))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ItemList)

        Here add and delete1 are the action Creators which create an action object for the dispatch method. we created add method for action type itemAdded which adds the items to the store with payload name and description.delete1 is an action creator method which creates action object for action itemRemoved and passes id through payload.

//action Creators
const add = (name,description) => ({ type: 'itemAdded',payload:{
         name:name,
         description:description
}});
const delete1 = (id) => ({ type: 'itemRemoved',payload:{
id:id
} });

        addItem and deleteItem are the functions returned by the mapDispatchToProps.using these two functions we dispatch the action to store.

//methods returned by mapDispatchToProps
deleteItem(id){
     this.props.deleteMyItem(id);
     alert("item deleted with id:"+id);
    }
addItem(event){
    event.preventDefault();
    this.props.addMyItem(this.state.name,this.state.description);
        alert("item "+this.state.name+" is added to store!!!");
    this.setState({
      name:"",
      description:""
    });
}
const mapDispatchToProps =  (dispatch) => {
  return {
    addMyItem:(name,description) => dispatch(add(name,description)),
    deleteMyItem: (id)=> dispatch(delete1(id))
  }
}

        matchStatetoProps function returns the current state of the store which we have used to display the list of items in the table.

//returns current state of the store
const mapStateToProps = (state) => {
    return {
        items:state,
    };
}

        To save the name & description we have used the state property of the class component and used onInputchange method to set the state whenever the user types in the name or description field.

onInputchange(event) {
    this.setState({
      [event.target.name]: event.target.value
    });
  }


5)React-Redux with Function Component:

        Unlike all class components, functional components don’t make use of connect API to connect to the redux store & also nothing is injected into component props as in-class components.
        For function components,react-redux packages provide a set of hooks that we use when writing code for the functional component.

        react-redux packages provide us two built-in hooks for accessing the store from react UI.namely useSelector & useDispatch.

1)useSelector:

        The first React-Redux hook that we’ll look at is the useSelector hook, which lets your React components read data from the Redux store.
        This hook is a replacement for the connect function’s mapStateToProps parameter.
        useSelector accepts a single function, which we call a selector function. A selector is a function that takes the entire Redux store state as its argument, reads some value from the state, and returns that result.

        The useSelector hook uses === to check whether the previously fetched value from the store is the same that we are currently getting. If that’s not the case, the component re-renders.

        now how the component knows the store state is updated? do we need to call store.subscribe() to listen for changes to the store in each component.

        fortunately, we have useSelector hook. useSelector automatically subscribes to the Redux store when any action is dispatched, redux will call its selector function forcing the component to re-render.
        If the value returned by the selector changes from the last time it ran, useSelector
will force our component to re-render with the new data.

const items = useSelector((state) => state);


2) useDispatch

        We now know how to read data from the Redux store into our components. But, how can we dispatch actions to the store from a component? usually, we call the store.dispatch(action) to dispatch the action against the store. but as we don’t have access to store directly in our component file, we need some way to get access to the dispatch function directly in our component file.
        The React-Redux useDispatch hook gives us the store’s dispatch method as its result.we call const dispatch = useDispatch() in any component that needs to dispatch actions
and then call dispatch(Action) to dispatch an action to the store.

const dispatch = useDispatch();


Provider:

        Our components can now read state from the store, and dispatch actions to the store. Where and how are the React-Redux hooks finding the right Redux store? A hook is a JS function, so it can’t automatically import a store from store.js by itself. we have to specifically tell React-Redux what store we want to use in our components. We do this by rendering a component around our entire <App> Component and passing the Redux store as a props to Provider Component.

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux';
import store from './store'

ReactDOM.render(
  <React.StrictMode>
  <Provider store={store}>
    <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);


Components:

  Our main Component is the ItemListFun component.ItemListFun component has mainly two sections one for adding Item to redux store and second for displaying items from the store.

import React,{useState} from 'react'
import './ItemList.css';
import { useSelector,useDispatch } from 'react-redux';

function  ItemListFun(){
        const items = useSelector(state=>state);
        const dispatch = useDispatch();
        const [name,setName] = useState('');
        const [description,setdescription] = useState('');

function deleteItem(id){
     dispatch(delete1(id));
     alert("item deleted with id:"+id);
  }

function addItem(event){
    event.preventDefault();
    dispatch(add(name,description));
    alert("item "+name+" is added to store!!!");
    setName('');
    setdescription('');
}

 const add = (name,description) => ({ type: 'itemAdded',payload:{
         name:name,
         description:description
}});
const delete1 = (id) => ({ type: 'itemRemoved',payload:{
id:id
} });

    return (
        <div className="container">
            <div className="container-flex">
               <div className="add-item">
                 <div className="item-heading">
                 <h2>Add Item</h2>
                 </div>
                 <form onSubmit={addItem}>
                 <div className="item-data">
                   <label>Item Name</label>
                   <input type="text" name="name" value={name} onChange={(e)=>setName(e.target.value)} id="name" placeholder="Enter item name" required/>
                   <label>Item Description</label>
                   <textarea placeholder="Item Description" value={description} onChange={(e)=>setdescription(e.target.value)} id="description" name="description" required></textarea>
                 </div>
                 <div className="item-button">
                 <input type="submit"/>
                 </div>
                 </form>
               </div>
               <div className="item-list">
                  <div  className="item-heading"> 
                  <h2>Item List</h2>
                  </div>
                  <div className="item-table">
                  <table>
                        <thead>
                        <tr>
                            <th>Item Id</th>
                            <th>Item Name</th>
                            <th>Item Description</th>
                            <th>Delete Item</th>
                        </tr>
                        </thead>
                        <tbody>
                            {items && items.map((item,index)=>{
                                return (
                                    <tr key={index}>
                                    <td>{item.id}</td>
                                    <td>{item.name}</td>
                                    <td>{item.description}</td>
                                     <td><button onClick={()=>deleteItem(item.id)}>Delete</button></td>
                                    </tr>
                                )}

                            )}
                        </tbody>
                    </table>
                    </div>
               </div>
            </div>
        </div>
    )
}


export default ItemListFun

we are available with addItem and deleteItem.using these two functions we dispatch the action to store.

function deleteItem(id){
     dispatch(delete1(id));
     alert("item deleted with id:"+id);
  }

function addItem(event){
    event.preventDefault();
    dispatch(add(name,description));
    alert("item "+name+" is added to store!!!");
    setName('');
    setdescription('');
}

our App component will look like,

import React from 'react'
import store from './store';
import ItemList from './ItemList'
import ItemListFun from './ItemListFun'
function App() {
    return (
        <div>
            <ItemListFun/>
        </div>
    )
}

export default App

Css for ItemListFun Component:

.container{
    display:grid;
    place-items: center;
    height: 97vh;
}
.container-flex{
    display: flex;
    flex:1;
    background-color: #f7f7f7;
    width: 90vw;
    height: 90vh;
    min-width: 90vw;
    box-shadow: -1px 4px 20px -6px rgba(0,0,0,0.75);
    min-height: 70vh;
    
}
.add-item{
    flex:0.4;
    display: flex;
    flex-direction: column;
}
.item-list{
    flex:0.6;
    display: flex;
    flex-direction: column;
}

.item-heading,.item-button,.item-data{
    display: grid;
    align-items: center;
    justify-content: center;
}
table {
    border: 1px solid;
  }
  th {
    background-color: #800080;
    color: white;
  }
  th,
  td {
    width: 150px;
    text-align: center;
    padding: 5px;
  }
 .item-data{
     height: 30vh;
     width: 40vw;
 }
 textarea{
     height: 70px;
 }
 input{
     height: 30px;
     width: 300px;
 }
 .item-button{
margin-top: 30px;
 }
 button{
     height: 40px;
     width: 100px;
 }
 label{
     font-size: 20px;
     font-weight: 500;
     font-family: cursive;
 }


Source Code:

Git Source code for React Redux class & function Component


Live Demo:

you can preview live demo at https://surajraccha.github.io/react-redux-demo/

Hope you guys like this post ,please share your thoughts in comment section!!!

Leave a Reply