ReactJS - State Management using React Hooks



React introduces an entirely new concepts called React Hooks from React 16.8. Even though, it is a relatively new concept, it enables React functional component to have its own state and life-cycle. Also, React Hooks enables functional component to use many of the feature not available earlier. Let us see how to do state management in a functional component using React Hooks in this chapter.

What is React Hooks?

React Hooks are special functions provided by React to handle a specific functionality inside a React functional component. React provides a Hook function for every supported feature. For example, React provides useState() function to manage state in a functional component. When a React functional component uses React Hooks, React Hooks attach itself into the component and provides additional functionality.

The general signature of useState() Hook is as follows −

const [<state variable>, <state update function>] = useState(<initial value>);

For example, state management in clock component using Hooks can be done as specified below −

const [currentDateTime, setCurrentDateTime] = useState(new Date()); 
setInterval(() => setCurrentDateTime(new Date()), 1000);

Here,

  • currentDateTime − Variable used to hold current date and time (returned by setState()
  • setCurrentDate() − Function used to set current date and time (returned by setState())

Create a stateful component

Let us recreate our clock component using Hooks in this chapter.

Step 1 − First, create a new react application, react-clock-hook-app using Create React App or Rollup bundler by following instruction in Creating a React application chapter.

Step 2 − Open the application in your favorite editor.

Create src folder under the root directory of the application.

Create components folder under src folder.

Create a file, Clock.js under src/components folder and start editing.

Import React library and React state Hook, setState.

import React, { useState } from 'react';

Step 2 − Create Clock component.

function Clock() { 
}

Create state Hooks to maintain date and time.

const [currentDateTime, setCurrentDateTime] = useState(new Date());

Set date and time for every second.

setInterval(() => setCurrentDateTime(new Date()), 1000);

Create the user interface to show the current date and time using currentDateTime and return it.

return ( <div><p>The current time is {currentDateTime.toString()}</p></div> );

Step 3 − Finally, export the component using the code snippet −

export default Clock;

The complete source code of the Clock component is as follows −

import React, { useState } from 'react';

function Clock(props) {
   const [currentDateTime, setCurrentDateTime] = useState(new Date());
   setInterval(() => setCurrentDateTime(new Date()), 1000);
   return (
      <div><p>The current time is {currentDateTime.toString()}</p></div>
   );
}
export default Clock;

index.js:

Next, create a file, index.js under the src folder and use Clock component.

import React from 'react';
import ReactDOM from 'react-dom';
import Clock from './components/Clock';

ReactDOM.render(
   <React.StrictMode>
      <Clock />
   </React.StrictMode>,
   document.getElementById('root')
);

Finally, create a public folder under the root folder and create index.html file.

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="utf-8">
      <title>Clock</title>
   </head>
   <body>
      <div id="root"></div>
      <script type="text/JavaScript" src="./index.js"></script>
   </body>
</html>

Then, serve the application using npm command.

npm start

Open the browser and enter http://localhost:3000 in the address bar and press enter. The application will show the time and update it every second.

The current time is Wed Nov 11 2020 10:10:18 GMT+0530 (India Standard Time)

The above application works fine. But, setCurrentDateTime() set to execute every second has to be removed when the application ends. We can do this using another Hook,useEffect provided by React. We will learn it in the upcoming chapter (Component life cycle).

Introducing state in expense manager app

Let us introduce state management in the expense manager application by adding a simple feature to remove an expenses item using Hooks in this chapter.

Step 1 − Open expense-manager application in your favorite editor.

Create a new file, ExpenseEntryItemListFn.js under src/components folder and start editing.

Import React library and React state Hook, setState.

import React, { useState } from 'react';

Import the css, ExpenseEntryItem.css.

import './ExpenseEntryItemList.css'

Step 2 − Create ExpenseEntryItemListFn component.

function ExpenseEntryItemListFn(props) { }

Initialize the state Hooks of the component with the expense items passed into the components through properties.

const [items, setItems] = useState(props.items);

Step 3 − Create event handlers to highlight the rows.

function handleMouseEnter(e) {
   e.target.parentNode.classList.add("highlight");
}
function handleMouseLeave(e) {
   e.target.parentNode.classList.remove("highlight");
}
function handleMouseOver(e) {
   console.log("The mouse is at (" + e.clientX + ", " + e.clientY + ")");
}

Step 4 − Create event handler to remove the selected items using items and setItems().

function handleDelete(id, e) {
   e.preventDefault();
   console.log(id);
   let newItems = [];
   items.forEach((item, idx) => {
      if (item.id != id)
         newItems.push(item)
   })
   setItems(newItems);
}

Step 5 − Create getTotal() method to calculate the total amount.

function getTotal() {
   let total = 0;
   for (var i = 0; i < items.length; i++) {
      total += items[i].amount
   }
   return total;
}

Step 6 − Create user interface to show the expenses by looping over the items.

const lists = items.map((item) =>
   <tr key={item.id} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
      <td>{item.name}</td>
      <td>{item.amount}</td>
      <td>{new Date(item.spendDate).toDateString()}</td>
      <td>{item.category}</td>
      <td><a href="#" onClick={(e) => handleDelete(item.id, e)}>Remove</a></td>
   </tr>
);

Step 7 − Create the complete UI to show the expenses and return it.

return (
   <table onMouseOver={handleMouseOver}>
      <thead>
         <tr>
            <th>Item</th>
            <th>Amount</th>
            <th>Date</th>
            <th>Category</th>
            <th>Remove</th>
         </tr>
      </thead>
      <tbody>
         {lists}
         <tr>
            <td colSpan="1" style={{ textAlign: "right" }}>Total Amount</td>
            <td colSpan="4" style={{ textAlign: "left" }}>
               {getTotal()}
            </td>
         </tr>
      </tbody>
   </table>
);

Finally, export the function as shown below −

export default ExpenseEntryItemListFn;

The complete code of the ExpenseEntryItemListFn is as follows −

import React, { useState } from 'react';
import './ExpenseEntryItemList.css'

function ExpenseEntryItemListFn(props) {
   const [items, setItems] = useState(props.items);

   function handleMouseEnter(e) {
      e.target.parentNode.classList.add("highlight");
   }
   function handleMouseLeave(e) {
      e.target.parentNode.classList.remove("highlight");
   }
   function handleMouseOver(e) {
      console.log("The mouse is at (" + e.clientX + ", " + e.clientY + ")");
   }
   function handleDelete(id, e) {
      e.preventDefault();
      console.log(id);
      let newItems = [];
      items.forEach((item, idx) => {
         if (item.id != id)
            newItems.push(item)
      })
      setItems(newItems);
   }
   function getTotal() {
      let total = 0;
      for (var i = 0; i < items.length; i++) {
         total += items[i].amount
      }
      return total;
   }
   const lists = items.map((item) =>
      <tr key={item.id} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
         <td>{item.name}</td>
         <td>{item.amount}</td>
         <td>{new Date(item.spendDate).toDateString()}</td>
         <td>{item.category}</td>
         <td><a href="#"
            onClick={(e) => handleDelete(item.id, e)}>Remove</a></td>
      </tr>
   );
   return (
      <table onMouseOver={handleMouseOver}>
         <thead>
            <tr>
               <th>Item</th>
               <th>Amount</th>
               <th>Date</th>
               <th>Category</th>
               <th>Remove</th>
            </tr>
         </thead>
         <tbody>
            {lists}
            <tr>
               <td colSpan="1" style={{ textAlign: "right" }}>Total Amount</td>
               <td colSpan="4" style={{ textAlign: "left" }}>
                  {getTotal()}
               </td>
            </tr>
         </tbody>
      </table>
   );
}
export default ExpenseEntryItemListFn;

index.js

Update the index.js and include the ExpenseEntyItemListFn component −

import React from 'react';
import ReactDOM from 'react-dom';
import ExpenseEntryItemListFn from './components/ExpenseEntryItemListFn'

const items = [
   { id: 1, name: "Pizza", amount: 80, spendDate: "2020-10-10", category: "Food" },
   { id: 2, name: "Grape Juice", amount: 30, spendDate: "2020-10-12", category: "Food" },
   { id: 3, name: "Cinema", amount: 210, spendDate: "2020-10-16", category: "Entertainment" },
   { id: 4, name: "Java Programming book", amount: 242, spendDate: "2020-10-15", category: "Academic" },
   { id: 5, name: "Mango Juice", amount: 35, spendDate: "2020-10-16", category: "Food" },
   { id: 6, name: "Dress", amount: 2000, spendDate: "2020-10-25", category: "Cloth" },
   { id: 7, name: "Tour", amount: 2555, spendDate: "2020-10-29", category: "Entertainment" },
   { id: 8, name: "Meals", amount: 300, spendDate: "2020-10-30", category: "Food" },
   { id: 9, name: "Mobile", amount: 3500, spendDate: "2020-11-02", category: "Gadgets" },
   { id: 10, name: "Exam Fees", amount: 1245, spendDate: "2020-11-04", category: "Academic" }
]
ReactDOM.render(
   <React.StrictMode>
      <ExpenseEntryItemListFn items={items} />
   </React.StrictMode>,
   document.getElementById('root')
);

Next, serve the application using npm command.

npm start

Next, open the browser and enter http://localhost:3000 in the address bar and press enter.

Finally, to remove an expense item, click the corresponding remove link. It will remove the corresponding item and refresh the user interface as shown in animated gif.

Interface
Advertisements