React Hooks Tutorial: How to Use Hooks in React.js App

by Didin J. on Aug 29, 2019 React Hooks Tutorial: How to Use Hooks in React.js App

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

The React Hooks tutorial on how to implement Hooks in a new React.js application that consume data from the REST API. Because hooks are a new addition in React 16.8, so, make sure your create-react-app application update to the latest version. Previously, we have introduced React Hooks in Ionic Framework 4 hybrid mobile apps with beta support of React.js.

A shortcut to the steps:

The React Hooks let you use state and other React features without writing a class. Hooks are functions to hook into React state and lifecycle features from function components. Hooks don’t work inside classes because hooks let you use React without classes.

The following tools, frameworks, modules, and libraries are required for this tutorial:

  1. Node.js (Recommended version)
  2. React.js
  3. Express.js and MongoDB REST API
  4. Terminal (Mac/Linux) or Node Command Line (Windows)
  5. IDE or Text Editor

We assume that you have already installed Node.js. Make sure Node.js command line is working (on Windows) or runnable in Linux/OS X terminal.

You can watch the video tutorial on our YouTube channel here.


Install and Create React App

The create-react-app is a tool to create a React.js app from the command line or CLI. So you don’t need to install or configure tools like Webpack or Babel because they are preconfigured and hidden so that you can focus on the code. Open the terminal or Node.js command line then go to your React.js projects folder. We will install React.js app creator for creating a React.js app easily. For that, type this create-react-app command.

sudo npm install -g create-react-app

Now, create a React app by type this command.

create-react-app react-hooks-app

This command will create a new React app with the name `react-firestore` and this process can take minutes because all dependencies and modules also installing automatically. Next, go to the newly created app folder.

cd ./react-hooks-app

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

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

That React version is the version that already uses React Hooks as default. Now, `src/App.js` doesn't use class anymore. For sanitation, run this React app for the first time by type this command.

yarn start

And here' we go the latest React.js application that uses React Hooks with the same initial home page.

React Hooks - React.js home screen


Add React Router DOM

We will use multiple pages for implementing CRUD operation for each REST API request method. To add the required components for CRUD operation type these commands including creating a new components folder.

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

Next, we have to install or add the required modules like react-route-dom for routing/navigation and React Bootstrap for styling the UI.

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

Next, open and edit `src/index.js` then add these imports of Router and Route (react-router-dom) before the `./index.css` import.

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

React Bootstrap component will be used in each created components individually. Unfortunately, the stylesheet or CSS does not include in the React Bootstrap installation. For that, open and edit `public/index.html` then include this Bootstrap CSS from CDN before the closing of <HEAD>.

<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"
  />

Also, import these created 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, add <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'));

Next, we will modify `src/App.js` to be a root of application with the React Bootstrap Navigation Bar. Add these imports before the `App.css` import.

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

Replace the existing render components with React Bootstrap Nav and Navbar components.

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 contains the navigation to root, list, and create routers.


React Hooks and Axios Fetch from REST API

To access or consume the REST API service, we will use Axios the promise-based HTTP client for the browser and node.js. Axios feature makes XMLHttpRequests, make HTTP requests, supports the Promise API, intercept requests and response, transform request and response data, cancel requests, automatic transforms for JSON data, client-side support for protecting against XSRF. To install the Axios HTTP client library, simply run this command.

npm install --save axios

We will use an existing `src/App.js` to display a list of data that fetch from the REST API. Open and modify `src/App.js` the add the React Hooks useState and useEffect to 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 with injected props.

function List(props) {

}

At the end of the file, add this export module as `withRouter` wrap the List function.

export default withRouter(List);

Next, fill the function bracket with the React Hooks useState that declare the data, setData, showLoading, setShowLoading constant variables. Also, declare a constant variable for REST API URL.

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

Add a React Hooks useEffect method or function that fetch data from REST API using Axios then set response to data constant variable.

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

  fetchData();
}, []);

Add a method or function to navigate to the details or show page with an ID parameter.

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

Implement the render components that contain a React Bootstrap ListGroup and Spinner which the ListGroup iterate data from the data constant of the React Hooks useState.

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 items in the List component are clickable and navigate to the Show Component. For that, open and edit `src/components/Show.js` then make similar imports, main function, export function, constant variables, methods, React Hooks useState, useEffect, and rendered component as List components except for this Show component only show a single data as an object. Also, use React Bootstrap Jumbotron to show details instead of ListGroup.

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 or functions that use in this Show Component is deleting product and navigation to Edit component.


React Hooks and Axios Post to REST API

Now, we will implement React Hooks Form. The Form component will use React Bootstrap Form and Form.Group. Open and edit `src/components/Create.js` then add these imports of React Hooks useState, useEffect, Axios, React Bootstrap Spinner, Jumbotron, Form, Button, and react-router-dom `withRouter`.

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';

Create main function with the injected props.

function Create(props) { }

In the end of file export that function and wrapped by `withRouter`.

export default withRouter(Create);

Declare the constant variables inside the main function bracket that implementing React Hooks useState and a REST API URL.

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";

Add a method or function to POST data to REST API using Axios then push to the Show component after successful response.

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));
};

Add a method or function that handles the OnChange event of the Form Input. Here, we will use a simple React Hooks style that set the state to a data or object.

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

Now, we will implement the rendered components that contain React Bootstrap Form, Form.Group, and Form.Control which is controlled by OnChange event.

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>
);

Attention: Don't forget to add the name to the Form.Control otherwise the OnChange event function doesn't work.


React Hooks and Axios Put to REST API

Similar to the previous React Hooks Form for POST data, the Form component for Edit data will use React Bootstrap Form and Form.Group. Open and edit `src/components/Edit.js` then add these imports of React Hooks useState, useEffect, Axios, React Bootstrap Spinner, Jumbotron, Form, Button, and react-router-dom `withRouter`.

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';

Create main function with the injected props.

function Create(props) { }

In the end of file export that function and wrapped by `withRouter`.

export default withRouter(Create);

Declare the constant variables inside the main function bracket that implementing React Hooks useState and a REST API URL.

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;

Add React Hooks useEffect to load or get data from the REST API using Axios then set the response to the product object.

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

  fetchData();
}, []);

Add a method or function to PUT data to REST API using Axios then push to the Show component after successful response.

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));
};

Add a method or function that handles the OnChange event of the Form Input. Here, we will use a simple React Hooks style that set the state to a data or object.

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

Now, we will implement the rendered components that contain React Bootstrap Form, Form.Group, and Form.Control which is controlled by OnChange event.

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 React Hooks Web App

Before running the React Hooks Web App, first, we have to run the MongoDB server and Node.js REST API server. Open a new Terminal tab then run the MongoDB daemon.

mongod

Open a new tab again then run the Node/Express REST API server.

nodemon

Now, you can run the React Hooks Web app in the current Terminal tab.

yarn start

Here's the full demo of the working application, you can watch in the video below.

 

That it's, React Hooks Tutorial: How to Use Hooks in React.js App. You can find the full working source code from our GitHub.

That just the basic. If you need more deep learning about MERN Stack, React.js or React Native you can take the following cheap course:

Thanks!