React - HTTP Client Programming



Let us use our newly created expense server in our Expense manager application in chapter React - Http Server through fetch() api.

The fetch() API

Let us create a new application to showcase client side programming in React.

First, create a new react application, react-http-app using Create React App or Rollup bundler by following instruction in Creating a React application chapter.

Next, open the application in your favorite editor.

Next, create src folder under the root directory of the application.

Next, create components folder under src folder.

Next, create a file, ExpenseEntryItemList.css under src/components folder and include generic table styles.

html {
   font-family: sans-serif;
}
table {
   border-collapse: collapse;
   border: 2px solid rgb(200,200,200);
   letter-spacing: 1px;
   font-size: 0.8rem;
}
td, th {
   border: 1px solid rgb(190,190,190);
   padding: 10px 20px;
}
th {
   background-color: rgb(235,235,235);
}
td, th {
   text-align: left;
}
tr:nth-child(even) td {
   background-color: rgb(250,250,250);
}
tr:nth-child(odd) td {
   background-color: rgb(245,245,245);
}
caption {
   padding: 10px;
}
tr.highlight td { 
    background-color: #a6a8bd;
}

Next, create a file, ExpenseEntryItemList.js under src/components folder and start editing.

Next, import React library.

import React from 'react';

Next, create a class, ExpenseEntryItemList and call constructor with props.

class ExpenseEntryItemList extends React.Component {
   constructor(props) {
      super(props);
   }
}

Next, initialize the state with empty list in the constructor.

this.state = {
   isLoaded: false,
   items: []
}

Next, create a method, setItems to format the items received from remote server and then set it into the state of the component.

setItems(remoteItems) {
   var items = [];
   remoteItems.forEach((item) => {
      let newItem = {
         id: item.id,
         name: item.name,
         amount: item.amount,
         spendDate: item.spend_date,
         category: item.category
      }
      items.push(newItem)
   });
   this.setState({
      isLoaded: true,
      items: items
   });
}

Next, add a method, fetchRemoteItems to fetch the items from the server.

fetchRemoteItems() {
   fetch("http://localhost:8000/api/expenses")
      .then(res => res.json())
      .then(
         (result) => {
            this.setItems(result);
         },
         (error) => {
            this.setState({
               isLoaded: false,
               error
            });
         }
      )
}

Here,

  • fetch api is used to fetch the item from the remote server.

  • setItems is used to format and store the items in the state.

Next, add a method, deleteRemoteItem to delete the item from the remote server.

deleteRemoteItem(id) {
   fetch('http://localhost:8000/api/expense/' + id, { method: 'DELETE' })
      .then(res => res.json())
      .then(
         () => {
            this.fetchRemoteItems()
         }
      )
}

Here,

  • fetch api is used to delete and fetch the item from the remote server.

  • setItems is again used to format and store the items in the state.

Next, call the componentDidMount life cycle api to load the items into the component during its mounting phase.

componentDidMount() { 
   this.fetchRemoteItems(); 
}

Next, write an event handler to remove the item from the list.

handleDelete = (id, e) => { 
   e.preventDefault(); 
   console.log(id); 

   this.deleteRemoteItem(id); 
}

Next, write the render method.

render() {
   let lists = [];
   if (this.state.isLoaded) {
      lists = this.state.items.map((item) =>
         <tr key={item.id} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.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) => this.handleDelete(item.id, e)}>Remove</a></td>
         </tr>
      );
   }
   return (
      <div>
         <table onMouseOver={this.handleMouseOver}>
            <thead>
               <tr>
                  <th>Item</th>
                  <th>Amount</th>
                  <th>Date</th>
                  <th>Category</th>
                  <th>Remove</th>
               </tr>
            </thead>
            <tbody>
               {lists}
            </tbody>
         </table>
      </div>
   );
}

Finally, export the component.

export default ExpenseEntryItemList;

Following is the complete code of ExpenseEntryItemList.js

import React from 'react';
import './ExpenseEntryItemList.css';

class ExpenseEntryItemList extends React.Component {
   constructor(props) {
      super(props);
      this.state = {
         isLoaded: false,
         items: []
      }
   }
   
   setItems(remoteItems) {
      var items = [];
      remoteItems.forEach((item) => {
         let newItem = {
            id: item.id,
            name: item.name,
            amount: item.amount,
            spendDate: item.spend_date,
            category: item.category
         }
         items.push(newItem)
      });
      this.setState({
         isLoaded: true,
         items: items
      });
   }
   
   fetchRemoteItems() {
      fetch("http://localhost:8000/api/expenses")
         .then(res => res.json())
         .then(
            (result) => {
               this.setItems(result);
            },
            (error) => {
               this.setState({
                  isLoaded: false,
                  error
               });
            }
      )
   }
   
   deleteRemoteItem(id) {
      fetch('http://localhost:8000/api/expense/' + id, { method: 'DELETE' })
         .then(res => res.json())
         .then(
            () => {
               this.fetchRemoteItems()
            }
         )
   }
   
   componentDidMount() { 
      this.fetchRemoteItems(); 
   }
   
   handleDelete = (id, e) => { 
      e.preventDefault(); 
      console.log(id); 

      this.deleteRemoteItem(id); 
   }
   
   
   render() {
      let lists = [];
      if (this.state.isLoaded) {
         lists = this.state.items.map((item) =>
            <tr key={item.id} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.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) => this.handleDelete(item.id, e)}>Remove</a></td>
            </tr>
         );
      }
      return (
         <div>
            <table onMouseOver={this.handleMouseOver}>
            <thead>
               <tr>
                  <th>Item</th>
                  <th>Amount</th>
                  <th>Date</th>
                  <th>Category</th>
                  <th>Remove</th>
               </tr>
            </thead>
            <tbody>
               {lists}
            </tbody>
            </table>
         </div>
      );
   }
}
export default ExpenseEntryItemList;

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

import React from 'react';
import { createRoot } from 'react-dom/client';
import "bootstrap/dist/css/bootstrap.min.css";
import ExpenseEntryItemList from './components/ExpenseEntryItemList';

const container = document.getElementById('root');
const root = createRoot(container);
root.render(<ExpenseEntryItemList  />);

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>React App</title>
   </head>
   <body>
      <div id="root"></div>
      <script type="text/JavaScript" src="./index.js"></script>
   </body>
</html>

Next, open a new terminal window and start our server application.

cd /go/to/server/application 
npm start

Next, serve the client application using npm command.

npm start

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

Material

Try to remove the item by clicking the remove link.

Materials
Advertisements