ReactJS (HTTP/AJAX): Asynchronous Interaction between React application and Server (REST API with axions)

React application interaction with server to fetch data and submitting data to server is very common for any enterprise web application. Like any application react application requires HTTP Library to interact with server. We have HTTP library available some are promise based and some are callback based. Fetch and Axios are promised based HTTP library and Superagent is callback based HTTP library.

In this post I will use Axios for executing HTTP methods like GET and POST. In order to simply server side setup I have used Firebase Realtime Database. Firebase deals with JSON data and persist it in tree structure.

HTTP call in React component life cycle: componenetDidMount() is right place from where HTTP methods are executed with precaution do not update state as it will trigger re-rendering cycle and HTTP call be executed in infinite loop.

Prerequisite

1. Install and setup axios
2. Setup Firebase real time database & sample data setup

Install axios
$ npm install axios

Firebase setup: Create a sample project at Firebase(Spark mode is free). I have created project "react-devinline". Under database tab -> create Realtime Database "devinlineaction". Firebase generates URL https://devinlineaction.firebaseio.com/ which act as end point to access data from Firebase realtime database.

Under Rules tab update read and write access. It makes data access without auth and prevents error like "Failed to load resource: the server responded with a status of 401 (Unauthorized)"
{
  "rules": {
    ".read": true,
    ".write": true
  }
}. 

Import below JSON data in Firebase database devinlineaction
 {
  "orders": [
    {
      "orderNo": "3330047777379",
       "status": "Delivered",
      "customerInfo": {
        "customerId": "832edb65-6ea2-40e4-bb07-8c3a2bacc159",
        "primaryContact": {
          "name": {
            "firstName": "Nikhil",
            "lastName": "Ranjan"
          },
          "phone": {
            "completeNumber": "408-660-5027"
          },
          "email": {
            "emailAddress": "nikhilranjan@email.com"
          }
        },
        "address": {
          "addressLineOne": "655 S Fair Oaks Avenue",
          "addressType": "RESIDENTIAL",
          "city": "Sunnyvale",
          "countryCode": "USA",
          "postalCode": "94086",
          "state": "CA"
        }
      },
      "orderLines": [
        {
          "productName": "Fujifilm Instax Mini 7S Instant Camera (with 10-pack film) - Light Blue",
          "lineno": 1,
          "orderedQty": {
            "unitOfMeasure": "EA",
            "measurementValue": 1
          },
          "unitPrice": {
            "currencyAmount": 125,
            "currencyUnit": "USD"
          }
        },
        {
          "productName": "Sumsung Monitor 45' ",
          "lineno": 1,
          "orderedQty": {
            "unitOfMeasure": "EA",
            "measurementValue": 1
          },
          "unitPrice": {
            "currencyAmount": 305,
            "currencyUnit": "USD"
          }
        },
        {
          "productName": "Wirelss mouse Dell-UQA765",
          "lineno": 1,
          "orderedQty": {
            "unitOfMeasure": "EA",
            "measurementValue": 2
          },
          "unitPrice": {
            "currencyAmount": 25,
            "currencyUnit": "USD"
          }
        }
      ]
    },
    {
      "orderNo": "3330047777380",
       "status": "Shipped",
      "customerInfo": {
        "customerId": "832edb65-6ea2-40e4-bb07-8c3a2bacc159",
        "primaryContact": {
          "name": {
            "firstName": "Nikhil",
            "lastName": "Ranjan"
          },
          "phone": {
            "completeNumber": "408-660-5027"
          },
          "email": {
            "emailAddress": "nikhilranjan@email.com"
          }
        },
        "address": {
          "addressLineOne": "655 S Fair Oaks Avenue",
          "addressType": "RESIDENTIAL",
          "city": "Sunnyvale",
          "countryCode": "USA",
          "postalCode": "94086",
          "state": "CA"
        }
      },
      "orderLines": [
        {
          "productName": "Purell Advanced - 8 fl Oz",
          "lineno": 1,
          "orderedQty": {
            "unitOfMeasure": "EA",
            "measurementValue": 1
          },
          "unitPrice": {
            "currencyAmount": 10,
            "currencyUnit": "USD"
          }
        },
        {
          "productName": "Mask-12 pc",
          "lineno": 1,
          "orderedQty": {
            "unitOfMeasure": "EA",
            "measurementValue": 1
          },
          "unitPrice": {
            "currencyAmount": 12,
            "currencyUnit": "USD"
          }
        }
      ]
    }
  ]
}

HTTP GET API using axios

Create a class based component "Orders.js" and a functional component "Order.js". Class based component Orders executes GET API and displays orders in UI using Order functional component.

[Orders.js]- Class based component which manages state. orderStatus fetches order# and status & using Order component data are displayed.

import React, { Component } from 'react';
import axios from 'axios';

import Order from './Order';

class Orders extends Component {
    state = {
        ordersStatus: [],
        error: false
    }

    deleteDataHandler = (_orderNo) => {  
    }

    componentDidMount() {
        let ordernoAndStatus = []
        axios.get('https://devinlineaction.firebaseio.com/orders.json' )
            .then(res => {
                const fetchedOrders = [];
                for (let key in res.data) {
                    let orderNo = res.data[key].orderNo;
                    let status = res.data[key].status;
                    ordernoAndStatus.push({orderNo,status})
                    fetchedOrders.push({
                        ...res.data[key],
                        id: key
                    });
                }
                this.setState({ordersStatus : ordernoAndStatus});
                console.log(fetchedOrders);
            })
            .catch(err => {
                this.setState({loading: false});
            });
            
    }

    render () {
        let orders = <p style={{textAlign: 'center'}}>
            Opps!! Something went wrong.</p>;
        if (!this.state.error) {
            orders = this.state.ordersStatus.map(orderStat => {
                return <Order 
                    key={orderStat.orderNo}
                    orderNo={orderStat.orderNo} 
                    status={orderStat.status}
                    deleteHandle=
                        {this.deleteDataHandler(orderStat.orderNo)} />;
            });
        }
        return (
            <div>
                <section>
                    <h1> Get API using axios: Orders status</h1>
                    {orders}
                </section>
            </div>
        );
    }
}

export default Orders;

[Order.js] - Order component is functional/presentational component to generate UI with order# and status. Based on status Cancel order button is displayed (if order is delivered -> button is not displayed)
import React from 'react';

const order = (props) => (
    <div>
            <h3>{props.orderNo}</h3>
            {props.status}
            {props.status !== 'Delivered' ? <div><button 
                style={{ background: '#f44336', 
                         border: 'none',
                         color: 'white',
                         padding: '8px 5px',
                         fontSize: '20px',
                         cursor:'pointer',
                         }} 
                onClick={props.deleteHandle}>Cancel Order</button></div> : null}
     <div style={{width: '700px',
                    margin:'auto',
                    height: '7px',
                    borderBottom: '1px solid black'}}></div>
     </div>
     
);

export default order;


[App.js]
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

import Orders from './containers/Orders'
import NewOrder from './components/NewOrder';

class App extends Component {
  
  render() {
    console.log('[App.js] render() execution');
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>

        {/* Render Child component Checkout */}
        <Orders/>
        <NewOrder/>
      </div>
    );
  }

}

export default App;


Start server using command : $ npm start and open http://localhost:3000/.
UI displaying order# and status & Console displaying GET Response

HTTP POST API using axios

[NewOrder.js] - HTTP Post method is executed on clicking create new Order button.

import React, { Component } from 'react';
import axios from 'axios';

class NewOrder extends Component {
    postDataHandler = () => {
        const data = {
            "orderNo": "3330047777381",
            "status": "Processing",
            "customerInfo": {
              "customerId": "832edb65-6ea2-40e4-bb07-8c3a2bacc159",
              "primaryContact": {
                "name": {
                  "firstName": "Nikhil",
                  "lastName": "Ranjan"
                },
                "phone": {
                  "completeNumber": "408-660-5028"
                },
                "email": {
                  "emailAddress": "nikhilranjan@email.com"
                }
              },
              "address": {
                "addressLineOne": "655 S Fair Oaks Avenue",
                "addressType": "RESIDENTIAL",
                "city": "Sunnyvale",
                "countryCode": "USA",
                "postalCode": "94086",
                "state": "CA"
              }
            },
            "orderLines": [
              {
                "productName": 
                    "Garmin vivoactive 3 Black with Stainless Hardware",
                "lineno": 1,
                "orderedQty": {
                  "unitOfMeasure": "EA",
                  "measurementValue": 1
                },
                "unitPrice": {
                  "currencyAmount": 149,
                  "currencyUnit": "USD"
                }
              }
            ]
          };
        axios.post('https://devinlineaction.firebaseio.com/orders.json', data)
            .then(response => {
                console.log(response);
                alert("Order Placed successfully !!")
            });
    }

    render () {
        return (
            <div>
                <h1> Post using axios: Place new order</h1>
                <button 
                    style={{ background: '#008CBA', 
                         border: 'none',
                         color: 'white',
                         padding: '15px 32px',
                         fontSize: '20px',
                         cursor:'pointer',
                         }} 
                    onClick={this.postDataHandler}>Create new Order</button>
            </div>
        );
    }
}

export default NewOrder;

Successful execution server respond with status code = 200

After refreshing UI - 3 orders are displayed in UI and Firebase database can be validated for new record.
Axios HTTP library can be used to execute DELETE,HEAD and PUT methods. It provides flexibility to configure request sent to server and handle server response. Error handling is also supported with axios.

4 Comments

Previous Post Next Post