Learn how to create React JS table with delete functionality using API and hooks
Tags: beginners, react, javascript
Published: 2020-05-25 21:09:50

---
title: Learn how to create React JS table with delete functionality using API and hooks
published: true
description: 
tags: beginners, react, javascript, reactjs
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/vr4rp8da909pxper7z76.jpg
---
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](https://dev.to/abdulbasit313/an-easy-way-to-create-a-customize-dynamic-table-in-react-js-3igg) article.*

### Let's begin
Hooks are just like functions. We call it the same as we call functions
```js
useState()
```
`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
```js
const [employees, setEmployees] = useState([])
```
its same as
```js
const array = useState([])
const employees = array[0]
const setEmployees = array[1]
```
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

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

        const response = await axios.get(url)
        console.log('response', response)
        setEmployees(response.data)
    }
```
We are using `axios` for http requests.
We need to download `axios` through terminal.
```
npm i axios
```
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.

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

### For Table Header
```js
const renderHeader = () => {
        let headerElement = ['id', 'name', 'email', 'phone', 'operation']

        return headerElement.map((key, index) => {
            return <th key={index}>{key.toUpperCase()}</th>
        })
    }
```
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

```js
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>
            )
        })
    }
```
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.
```js
return (
        <div>
            <h1 id='title'>React Table</h1>
            <table id='employee'>
                <thead>
                    <tr>{renderHeader()}</tr>
                </thead>
                <tbody>
                    {renderBody()}
                </tbody>
            </table>
        </div>
    )
}
```
### 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.*

```js
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)
        })
    }
```

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.

```js
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 }
}
```
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

```js
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
```

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

### Styling

```css
@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;
}
```
*Codepen Link*
This is the codepen demo of the project

{% codepen https://codepen.io/Basit600/pen/oNjOBoB %}