Search for:

The React Hooks tutorial on how to implement Hooks in a new React.js application that consume data from the REST API




This article is focused mainly on how to implement Hooks in a React JS app that consumes data from the REST API.
Because Hooks are the new addition in React 16.8, make sure you create a react-app update to the latest version.
Basically, React Hooks let you use state and other React features without writing a class. Hooks are functions to hook in React state, and lifecycle features from function components.
Originally, Hooks didn’t work inside classes because hooks let you use React without classes.
The tools below are required for this tutorial:
  1. Express.js
  2. Terminal
  3. IDE 
  4. Node.js
  5. React JS

Assuming you already have Node.js, make sure the command line is working or runnable.

Install and create a React app

The create-react-app is a tool helping you create a React JS app from the command line. So, you don’t need to install tools like Webpack or Babel – they are preconfigured and hidden allowing you to focus only on the code.

Open the terminal and go to your React JS projects folder. You will install the React JS app creator for creating a React JS app very easy.

Type this create-react-app command

sudo npm install -g create-react-app

Now, create a React JS app by typing this command

create-react-app react-hooks-app

This command creates a brand new React JS with the name react-firestore and this might take minutes depending on the dependencies.

Next, go to the newly created app folder

cd ./react-hooks-app

Open your project in your IDE and see the content of package.json

"dependencies": {
  "react": "^16.9.0",
  "react-dom": "^16.9.0",
  "react-scripts": "3.1.1"
},

That React JS version is the version that already uses React Hooks as default. Now, the App.js doesn’t use class anymore!

For the sake of it, run this React JS app for the first time using this command

yarn start

And, here we go!

You should see a screen like this

React JS app
React JS app initial screen

Add the React Router DOM

In this example, we have to use multiple pages in order to implement the CRUD operation for the REST API requests.




To add the required components for the CRUD operation, type in these commands

mkdir src/components
touch src/components/List.js
touch src/components/Create.js
touch src/components/Show.js
touch src/components/Edit.js

Next, you have to install the required modules like react-router-dom for navigation and React Bootstrap for the sake of styling.

yarn add react-route-dom
yarn add react-bootstrap bootstrap

Next, open and edit the index.js, and add these imports of Router and Route, right before the ./index.css import.

import { BrowserRouter as Router, Route } from 'react-router-dom';

The React Bootsrap component will be used in every component individually. Actually, the stylesheet doesn’t include in the React JS Boostrap installation.

For that, open and edit public/index.html and include this before the closing HEAD tag.

<link
    rel="stylesheet"
    href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
    integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
    crossorigin="anonymous"
  />

Additionally, import these components after the registerServiceWorker.

import List from './components/List';
import Edit from './components/Edit';
import Create from './components/Create';
import Show from './components/Show';

Next in line is the Router to the React.DOM render

ReactDOM.render(
    <Router>
        <div>
            <Route render ={()=> < App />} path="/" />
            <Route render ={()=> < List />} path="/list" />
            <Route render ={()=> < Edit />} path="/edit/:id" />
            <Route render ={()=> < Create />} path="/create" />
            <Route render ={()=> < Show />} path="/show/:id" />
        </div>
    </Router>, document.getElementById('root'));

After that, you will need to modify scr/App.js to be the root of the app with the React Bootstrap Navigation Bar. You can do so by importing these before the App.css import

import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';

Also, replace the existing render component with this:

return (
  <Navbar bg="light" expand="lg">
    <Navbar.Brand href="/">React-Hooks</Navbar.Brand>
    <Navbar.Toggle aria-controls="basic-navbar-nav" />
    <Navbar.Collapse id="basic-navbar-nav">
      <Nav className="mr-auto">
        <Nav.Link href="/">Home</Nav.Link>
        <Nav.Link href="/list">List of Products</Nav.Link>
        <Nav.Link href="/create">Add Product</Nav.Link>
      </Nav>
    </Navbar.Collapse>
  </Navbar>
);

The Navbar will contain the navigation to roo,t list and create them.

React Hooks and Axios Fetch

In order to access the REST API service, you will have to use Axios – the promise-based HTTP client for the browser.




Axios features take care of

  • The XMLHttpRequest
  • Makin all the HTTP requests
  • Support the Promise API
  • Intercept requests
  • Transform requests
  • Cancel requests
  • Automatically transforming for JSON data

To install Axios HTTP, run this command

npm install --save axios

You will use the existing App.js file to display a list of the data fetched from the API.

Go on and modify the App.js and add the React Hooks useState and useEffect to the existing React import, and add imports of Axios, React Bootstrap ListGroup, Spinner and react-router-dom – withRouter

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import ListGroup from 'react-bootstrap/ListGroup';
import Spinner from 'react-bootstrap/Spinner';
import { withRouter } from 'react-router-dom';

Create the main function of the list like this

function List(props) {

}

At the end of the file, add this export

export default withRouter(List);

Next up, you have to fill the function bracket with the React Hooks useState that declares the data, setData, showLoading, setShowLoading constant variables.

Additionally, declare a constant variable for the API URL.

const [data, setData] = useState([]);
const [showLoading, setShowLoading] = useState(true);
const apiUrl = "http://localhost:3000/api/v1/products";

Add the React Hooks useEffect method that fetches data from the API using Axios

useEffect(() => {
  const fetchData = async () => {
    const result = await axios(apiUrl);
    setData(result.data);
    setShowLoading(false);
  };

  fetchData();
}, []);




Also, add a method to navigate to the details page with an ID as a parameter

const showDetail = (id) => {
  props.history.push({
    pathname: '/show/' + id
  });
}

Implement the render component that contains a Spinner and React Bootstrap ListGroup which will iterate the data from the data constant

return (
  <div>
    {showLoading && <Spinner animation="border" role="status">
      <span className="sr-only">Loading...</span>
    </Spinner> }
    <ListGroup>
      {data.map((item, idx) => (
        <ListGroup.Item key={idx} action onClick={() => { showDetail(item._id) }}>{item.prod_name}</ListGroup.Item>
      ))}
    </ListGroup>
  </div>
);

The list item components are clickable and navigate you to the Show Component.

In order to do that, open the src/components/Show.js file, and make similar imports, main function, export function and everything else like below.

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Spinner from 'react-bootstrap/Spinner';
import Jumbotron from 'react-bootstrap/Jumbotron';
import Button from 'react-bootstrap/Button';
import { withRouter } from 'react-router-dom';

function Show(props) {
  const [data, setData] = useState({});
  const [showLoading, setShowLoading] = useState(true);
  const apiUrl = "http://localhost:3000/api/v1/products/" + props.match.params.id;

  useEffect(() => {
    setShowLoading(false);
    const fetchData = async () => {
      const result = await axios(apiUrl);
      setData(result.data);
      setShowLoading(false);
    };

    fetchData();
  }, []);

  const editProduct = (id) => {
    props.history.push({
      pathname: '/edit/' + id
    });
  };

  const deleteProduct = (id) => {
    setShowLoading(true);
    const product = { prod_name: data.prod_name, prod_desc: data.prod_desc, prod_price: parseInt(data.prod_price) };
    axios.delete(apiUrl, product)
      .then((result) => {
        setShowLoading(false);
        props.history.push('/list')
      }).catch((error) => setShowLoading(false));
  };

  return (
    <div>
      {showLoading && <Spinner animation="border" role="status">
        <span className="sr-only">Loading...</span>
      </Spinner> }
      <Jumbotron>
        <h1>{data.prod_name}</h1>
        <p>{data.prod_desc}</p>
        <h2>Price: ${data.prod_price}</h2>
        <p>
          <Button type="button" variant="primary" onClick={() => { editProduct(data._id) }}>Edit</Button>&nbsp;
          <Button type="button" variant="danger" onClick={() => { deleteProduct(data._id) }}>Delete</Button>
        </p>
      </Jumbotron>
    </div>
  );
}

export default withRouter(Show);




The methods and functions used in this component are deleting the product and navigating to the Edit component.

React Hooks and Axios Post to the API

After that, we will implement the React Hooks Form. Finally!

The Form component will use the React Bootstrap Form, and Form.Group.

Open up the scr/components/Create.sj file and add these imports like below:

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Spinner from 'react-bootstrap/Spinner';
import Jumbotron from 'react-bootstrap/Jumbotron';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import { withRouter } from 'react-router-dom';

Next, create a main function with the injected props

function Create(props) { }

In the end, export the function wrapper by the withRouter

export default withRouter(Create);

Next up, declare the constant variables inside the main function that are currently implementing the React Hooks useState

const [product, setProduct] = useState({ _id: '', prod_name: '', prod_desc: '', prod_price: 0 });
const [showLoading, setShowLoading] = useState(false);
const apiUrl = "http://localhost:3000/api/v1/products";

Additionally add a method to Post the data to the API using Axios, like this:

const saveProduct = (e) => {
  setShowLoading(true);
  e.preventDefault();
  const data = { prod_name: product.prod_name, prod_desc: product.prod_desc, prod_price: parseInt(product.prod_price) };
  axios.post(apiUrl, data)
    .then((result) => {
      setShowLoading(false);
      props.history.push('/show/' + result.data._id)
    }).catch((error) => setShowLoading(false));
};

Next, add a method that handles all the OnChange events, using a simple React Hooks style that sets the state to an object

const onChange = (e) => {
  e.persist();
  setProduct({...product, [e.target.name]: e.target.value});
}

Now, implement the rendered component that contains the React Bootstrap Form, and other packages like this:

return (
  <div>
    {showLoading &&
      <Spinner animation="border" role="status">
        <span className="sr-only">Loading...</span>
      </Spinner>
    }
    <Jumbotron>
      <Form onSubmit={saveProduct}>
        <Form.Group>
          <Form.Label>Product Name</Form.Label>
          <Form.Control type="text" name="prod_name" id="prod_name" placeholder="Enter product name" value={product.prod_name} onChange={onChange} />
        </Form.Group>
        <Form.Group>
          <Form.Label>Product Description</Form.Label>
          <Form.Control as="textarea" name="prod_desc" id="prod_desc" rows="3" placeholder="Enter product description" value={product.prod_desc} onChange={onChange} />
        </Form.Group>
        <Form.Group>
          <Form.Label>Product Price</Form.Label>
          <Form.Control type="number" name="prod_price" id="prod_price" placeholder="Enter product price" value={product.prod_price} onChange={onChange} />
        </Form.Group>
        <Button variant="primary" type="submit">
          Save
        </Button>
      </Form>
    </Jumbotron>
  </div>
);

NOTE: Don’t forget to add the name of the Form.Control or the function won’t work.




React Hooks and Axios Put

Almos identically like the previous React Hooks Form for Post data, the Edit component will use React Bootstrap Form, and Form.Group. Open up the scr/components/Edit.js file and  add these imports like below:

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Spinner from 'react-bootstrap/Spinner';
import Jumbotron from 'react-bootstrap/Jumbotron';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import { withRouter } from 'react-router-dom';

Same as above, add the main function

function Create(props) { }

And in the end, add the export line

export default withRouter(Create);

Declare the constant

const [product, setProduct] = useState({ _id: '', prod_name: '', prod_desc: '', prod_price: 0 });
const [showLoading, setShowLoading] = useState(true);
const apiUrl = "http://localhost:3000/api/v1/products/" + props.match.params.id;

variables inside the main function like this

const [product, setProduct] = useState({ _id: '', prod_name: '', prod_desc: '', prod_price: 0 });
const [showLoading, setShowLoading] = useState(true);
const apiUrl = "http://localhost:3000/api/v1/products/" + props.match.params.id;

Next up, add the React Hooks useEffect to help load the data from the REST API using Axios

useEffect(() => {
  setShowLoading(false);
  const fetchData = async () => {
    const result = await axios(apiUrl);
    setProduct(result.data);
    console.log(result.data);
    setShowLoading(false);
  };

  fetchData();
}, []);

After that, add a method to PUT data to the said REST API and then push to the Show component if the response is successful

const updateProduct = (e) => {
  setShowLoading(true);
  e.preventDefault();
  const data = { prod_name: product.prod_name, prod_desc: product.prod_desc, prod_price: parseInt(product.prod_price) };
  axios.put(apiUrl, data)
    .then((result) => {
      setShowLoading(false);
      props.history.push('/show/' + result.data._id)
    }).catch((error) => setShowLoading(false));
};

Additionally, add a method that handles the OnChange event of the Form input

const onChange = (e) => {
  e.persist();
  setProduct({...product, [e.target.name]: e.target.value});
}

Next, you will have to implement the rendered components that contain the React Bootstrap form and other packages like this:

return (
  <div>
    {showLoading &&
      <Spinner animation="border" role="status">
        <span className="sr-only">Loading...</span>
      </Spinner>
    }
    <Jumbotron>
      <Form onSubmit={updateProduct}>
        <Form.Group>
          <Form.Label>Product Name</Form.Label>
          <Form.Control type="text" name="prod_name" id="prod_name" placeholder="Enter product name" value={product.prod_name} onChange={onChange} />
        </Form.Group>
        <Form.Group>
          <Form.Label>Product Description</Form.Label>
          <Form.Control as="textarea" name="prod_desc" id="prod_desc" rows="3" placeholder="Enter product description" value={product.prod_desc} onChange={onChange} />
        </Form.Group>
        <Form.Group>
          <Form.Label>Product Price</Form.Label>
          <Form.Control type="number" name="prod_price" id="prod_price" placeholder="Enter product price" value={product.prod_price} onChange={onChange} />
        </Form.Group>
        <Button variant="primary" type="submit">
          Update
        </Button>
      </Form>
    </Jumbotron>
  </div>
);




Run and Test the App

It’s testing time!

Before you run the React Hooks Web App, you have to make sure to run the MongoDB server and the Node JS REST API server.

Open a new tab in your terminal and add the following:

mongod

Open another tab and run the Node REST API server

nodemon

Now you can run the React Hooks Webb application in the current tab

yarn start

And, that’s it!

 

Write A Comment

Shares