Learn how to create React JS table with delete functionality using API and hooks
Abdul Basit

Abdul Basit @abdulbasit313

About: MERN Stack Developer at Orange Fox Labs

Location:
Karachi, Pakistan
Joined:
Mar 10, 2019

Learn how to create React JS table with delete functionality using API and hooks

Publish Date: May 25 '20
65 20

In this article, I will be using rest API ( JSON placeholder ) with fake data instead of static js object. Real apps work with APIs.

If you want to see the class based approach read this article.

Let's begin

Hooks are just like functions. We call it the same as we call functions

useState()
Enter fullscreen mode Exit fullscreen mode

useState accept an argument that is initial value for the state and return two things current value and method for update the state property.

Declaing State

const [employees, setEmployees] = useState([])
Enter fullscreen mode Exit fullscreen mode

its same as

const array = useState([])
const employees = array[0]
const setEmployees = array[1]
Enter fullscreen mode Exit fullscreen mode

We wrote it in one line using array destructuring.

We define employees as an empty array, as soon we will hit the API, the state will change and put all the API data into employees array.

Get Data Function

const getData = async () => {
        let url = 'https://jsonplaceholder.typicode.com/users'

        const response = await axios.get(url)
        console.log('response', response)
        setEmployees(response.data)
    }
Enter fullscreen mode Exit fullscreen mode

We are using axios for http requests.
We need to download axios through terminal.

npm i axios
Enter fullscreen mode Exit fullscreen mode

We made the getData function async because it takes some time to fetch data from API. So we said wait until the data is loaded then save it in response variable.

In previous article we called api in componentDidMount but useEffect hook has replaced componentDidMount, useEffect is easier to read, and write.

useEffect(() => {
        getData()
    }, []) // don't forget this empty bracket it indicates the function will only run once when the component will load initially
Enter fullscreen mode Exit fullscreen mode

For Table Header

const renderHeader = () => {
        let headerElement = ['id', 'name', 'email', 'phone', 'operation']

        return headerElement.map((key, index) => {
            return <th key={index}>{key.toUpperCase()}</th>
        })
    }
Enter fullscreen mode Exit fullscreen mode

First of all, we will decide how many columns we need for our table and then define these values in an array. In our case we need 5 columns, now we will map over these values and output as th

Operation is for Edit and Delete functionality.

For Table Body

const renderBody = () => {
        return employees && employees.map(({ id, name, email, phone }) => {
            return (
                <tr key={id}>
                    <td>{id}</td>
                    <td>{name}</td>
                    <td>{email}</td>
                    <td>{phone}</td>
                    <td className='opration'>
                        <button onClick={() => removeData(id)}>Delete</button>
                    </td>
                </tr>
            )
        })
    }
Enter fullscreen mode Exit fullscreen mode

Here you may have noticed the logic employees && employees.map, we are saying employees.map will only run if we have employee. Because it took some seconds to load the data from server, and if we will not write this logic our code will break, because employees array will be empty initially and map will not run on empty array, it won't have id, name and rest of the field, so it will through error.

Don't get confused with { id, name, email, phone } we just destructured the object.

We are also passing id as a parameter to removeData method.

Main return function

We just called both of our methods in our main return function.

return (
        <div>
            <h1 id='title'>React Table</h1>
            <table id='employee'>
                <thead>
                    <tr>{renderHeader()}</tr>
                </thead>
                <tbody>
                    {renderBody()}
                </tbody>
            </table>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

Delete functionality

We can send four kinds of requests via axios

  1. get
  2. post
  3. delete
  4. put

For delete, we will send a delete request. As the name says we use this method to delete a record on the backend.

delete takes the url as a parameter with the specific id of the record we want to delete. We will send id as a parameter.

Some of the time JSON placeholder API don't accept delete request and you won't feel the change on frontend this is just for demo purpose.

const removeData = (id) => {
        let url = `https://jsonplaceholder.typicode.com/users/${id}`

        axios.delete(url).then(res => {
            const del = employees.filter(employee => id !== employee.id)
            setEmployees(del)
            console.log('res', res)
        })
    }
Enter fullscreen mode Exit fullscreen mode

In our case, we can see data will be deleted on frontend but not on the backend. Because we can not manipulate JSON placeholder API. But if we have our own API that has delete feature too. It would just work fine.

To show the user, the data has been deleted we filtered the deleted object from frontend using higher-order filter method.

Refactoring Code

Organizing and refactoring code is essential. You may have noticed we have used URL in two different places, what if in future we need to change the URL? are we going to change from both places? no, we should have one common place where we define URL.

  • In real projects, we have a config file for that but for this one component base app, I will define URL on top of the file.
  • We also have to remove the consoles it was only for testing purposes.
  • We don't want initial div. For that, we will just use an empty bracket.

useApi custom hook

we can clean up our component and make code modular, just switching all of our business logic to a custom hook, useApi hook can be used in multiple places in our app.

import { useState, useEffect } from 'react'
import axios from 'axios'

export function useAPi(url) {
  const [data, setData] = useState([])

  useEffect(() => {
    getData()
  }, [])

  const getData = async () => {
    const response = await axios.get(url)
    setData(response.data)
  }

  const removeData = (id) => {
    axios.delete(`${url}/${id}`).then(() => {
      const del = data.filter((item) => id !== item.id)
      setData(del)
    })
  }

  return { data, removeData }
}
Enter fullscreen mode Exit fullscreen mode

This is straightforward

  1. We will pass API url as params.
  2. Returns the data (this is our employee data) and removeData function.

Complete Code

import React from 'react'
import { useAPi } from '../../hooks/useApi'

const URL = 'https://jsonplaceholder.typicode.com/users'

const Table = () => {
  const { data, removeData } = useAPi(URL)

  const renderHeader = () => {
    let headerElement = ['id', 'name', 'email', 'phone', 'operation']

    return headerElement.map((key, index) => {
      return <th key={index}>{key.toUpperCase()}</th>
    })
  }

  const renderBody = () => {
    return (
      data &&
      data.map(({ id, name, email, phone }) => {
        return (
          <tr key={id}>
            <td>{id}</td>
            <td>{name}</td>
            <td>{email}</td>
            <td>{phone}</td>
            <td className="operation">
              <button className="button" onClick={() => removeData(id)}>
                Delete
              </button>
            </td>
          </tr>
        )
      })
    )
  }

  return (
    <>
      <h1 id="title">React Table</h1>
      <table id="employee">
        <thead>
          <tr>{renderHeader()}</tr>
        </thead>
        <tbody>{renderBody()}</tbody>
      </table>
    </>
  )
}

export default Table
Enter fullscreen mode Exit fullscreen mode

Isn't it much cleaner? our business logic is completely separated now.

Styling

@import url("https://fonts.googleapis.com/css2?family=Quicksand:wght@500&display=swap");

body {
  font-family: "Quicksand", sans-serif;
  display: flex;
  justify-content: center;
  padding: 0;
  color: #4d4d4d;
}

#title {
  text-align: center;
}

#employee {
  border-collapse: collapse;
  border: 3px solid #ddd;
}

#employee td,
#employee th {
  border: 1px solid #ddd;
  padding: 12px;
}

#employee tr:hover {
  background-color: #ddd;
}

#employee th {
  padding: 10px;
  text-align: center;
  background-color: #4caf50;
  color: white;
}

.operation {
  text-align: center;
}

.button {
  border: none;
  outline: none;
  font-size: 11px;
  font-family: "Quicksand", sans-serif;
  color: #f44336;
  padding: 3px 10px;
  border-radius: 8px;
  cursor: pointer;
  border: 1px solid #f44336;
  background-color: transparent;
}

.button:active {
  border: 1px solid blue;
}
Enter fullscreen mode Exit fullscreen mode

Codepen Link
This is the codepen demo of the project

Comments 20 total

  • Will G
    Will GMay 26, 2020

    Nice post!

  • lavanya
    lavanyaJun 25, 2020

    Nice One,Similarly can we add edit function also inside the table,please do let me know whether it can be done.

    • Abdul Basit
      Abdul BasitJun 25, 2020

      Yes beside delete, create Edit button, then for edit either you need to have form for edit or just replace each cell of row into input and edit the value there.

      • lavanya
        lavanyaJun 26, 2020

        Thanks for your reply,it was useful :),Keep posting more topics like this

      • lavanya
        lavanyaJun 27, 2020

        So i just tried this for our project,when i try to edit I got struck with edit functionality
        i'm not able to edit the rows based on the id.Please give us some idea to proceed on
        :)

        • Abdul Basit
          Abdul BasitJun 27, 2020

          What's your approach? Did you create the form for add and edit data?

          • lavanya
            lavanyaJun 27, 2020

            1.We are not planning to create a form as of now,we like to edit it in the table itself.
            2.If we want to create a form for editing how can we do it,
            Note : Currently we are using React-Router Package for routing purpose,can we use the same package or any other suggestions....
            3.How to apply Responsiveness in a easy way.?

            It would be really great if you can help us with these questions so that we can proceed on with our project.

            • Abdul Basit
              Abdul BasitJun 27, 2020

              For first approach, define a key isEditing in the state that would be false initially. When you click the edit button the isEditing will become true. So now you can render conditionally, if isEditing is true, render the input filled with editable values, otherwise render table row. You may find this implementation on codepen.
              As far as the responsiveness concern you can use react responsive table package.

              • brmuser9999
                brmuser9999Jul 13, 2020

                what If I want to route to a different url when view button is clicked to view entire details(initially only id and name is shown and onclick should render a component where all details are seen) based on the passed id .I am not able to route to different component ?

                • brmuser9999
                  brmuser9999Jul 14, 2020

                  Now suppose I click any of the view buttons from any row ,pass its id to onclick function... do a state change ..set the id to ID and conditionally do routing when ID is set:true ..does that work ?

      • Hina Qammar
        Hina QammarApr 14, 2022

        delete a row from db table on clicking button using redux??

  • Ronaldo Maia Correa
    Ronaldo Maia CorreaAug 12, 2020

    Hi Abdul,

    I am kinda beginner on React. It is possible to have a page with one button, then when I press the button I render the table with one row, then I add more rows each time I press the button

    • Abdul Basit
      Abdul BasitAug 13, 2020

      Yes. It is... You will add data to your state on pressing button.

  • Kenneth Wardlow
    Kenneth WardlowAug 18, 2020

    Were you going to be adding the examples of this with a sortable table possibly by clicking on the headers to sort alphabetically or a search to narrow down with key characters

    • Abdul Basit
      Abdul BasitAug 18, 2020

      Not that difficult. Just make a sorting function and call it on click where you want.
      Material ui and antd table already have sorting functionality you can use that too.

      • Kenneth Wardlow
        Kenneth WardlowAug 18, 2020

        Thanks it's my first react project so wrapping my head around all of it still lol

  • Ali
    AliNov 2, 2020

    Thank you Basit Bhai :)

  • RachayyaMathapati
    RachayyaMathapatiDec 5, 2021

    This code is reuable table

  • Salimeh1364
    Salimeh1364Dec 9, 2021

    hello , tnx in advance , I am new in react I would to know how can I add pagination to this table?

  • Hina Qammar
    Hina QammarApr 14, 2022

    delete a row from db table on clicking button using redux??

Add comment