Form and Table
Background
In this doc, we'll learn how to create a form and a table using React. The form will take user input and send it to a server via an API, while the table will display data fetched from the server. If the server isn't available, we'll use a default array to populate the table and submit the form data locally, since the array doesn't persist data, it will be lost on page refresh.
Basic Setup
To start, let's create a new React project. If you already have a project set up, feel free to skip this step.
npx create-react-app form-and-table
cd form-and-table
First, we'll focus on the design. We'll create a form to add a book and a table to display the list of books. Once the design is complete, we'll move on to the backend integration using APIs.
Let's start by creating the form.
Creating a Form
Form fields:
- Name
- Author
- Price
- Stock
Will use the <form> tag to structure the form and the <input> tag to create input fields. The form submission will be handled by the onSubmit event.
Here’s how to create the form in the App.js file:
import React from "react";
function App() {
const onSubmit = async (e) => {
// Prevents page from refreshing on form submission
e.preventDefault();
// Create a book object from form data
const book = {
name: e.target.name.value,
author: e.target.author.value,
price: parseInt(e.target.price.value),
stock: parseInt(e.target.stock.value),
};
console.log("Form submitted", book);
// Code to send data to the server will be added here
};
return (
<div className="App">
<h1>Book Store</h1>
<h2>Add Book</h2>
<form onSubmit={onSubmit}>
<input type="text" name="name" placeholder="Name" />
<input type="text" name="author" placeholder="Author" />
<input type="number" name="price" placeholder="Price" />
<input type="number" name="stock" placeholder="Stock" />
<input type="submit" value="Save" />
</form>
</div>
);
}
export default App;
This form allows users to add a book. The form submission is handled by the onSubmit event, which creates a book object with the provided details. I have not added the code to send data to the server yet, but we'll do that later.
Creating a Table
Next, we’ll create a table to display the list of books.
The table will be structured using the <table>, <thead>, <tr>, <th>, <tbody>, and <td> tags. We'll use React's useState hook to store the list of books and useEffect to fetch data from the server when the component mounts. The map function will be used to iterate over the book list and display the books dynamically in the table.
Here's how to implement this in the App.js file:
import "./App.css";
import React from "react";
function App() {
const [books, setBooks] = React.useState([]);
React.useEffect(() => {
// Fetch book list from the server or use default data
setBooks([
{
_id: "1",
name: "Book 1",
author: "Author 1",
price: 100,
stock: 10,
},
]);
}, []);
const onSubmit = async (e) => {
e.preventDefault();
const book = {
name: e.target.name.value,
author: e.target.author.value,
price: parseInt(e.target.price.value),
stock: parseInt(e.target.stock.value),
};
console.log("Form submitted", book);
// Code to send data to the server will be added here
};
const deleteBook = async (id) => {
// Code to delete book will be added here
};
return (
<div className="App">
<h1>Book Store</h1>
<h2>Add Book</h2>
<form onSubmit={onSubmit}>
<input type="text" name="name" placeholder="Name" />
<input type="text" name="author" placeholder="Author" />
<input type="number" name="price" placeholder="Price" />
<input type="number" name="stock" placeholder="Stock" />
<button type="submit">Save</button>
</form>
<h2>Books</h2>
<table>
<thead>
<tr>
<th>Book Name</th>
<th>Author</th>
<th>Price</th>
<th>Stock</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{books.map((book) => (
<tr key={book._id}>
<td>{book.name}</td>
<td>{book.author}</td>
<td>{book.price}</td>
<td>{book.stock}</td>
<td>
<button onClick={() => deleteBook(book._id)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default App;
We’ve now added a table to display the list of books, using the map function to render each book in the table. We’ve also included a delete button, though the code for this functionality will be added later.
Now that the design is complete, let’s move on to integrating the backend APIs.
API Integration
We'll use the axios library to interact with the backend API. Let's install axios first:
npm install axios
axios is a popular library for making HTTP requests. We’ll use it to make GET, POST, and DELETE requests to our backend.
axios.get
The axios.get function is used to make GET requests. It takes the API URL as its first argument and an optional configuration object for headers or other settings. The function returns a Promise, allowing us to handle the response with .then().
Example:
const headers = {
Authorization: "Bearer <token>",
};
axios
.get("https://api.github.com/users/octocat", { headers })
.then((response) => {
console.log(response.data);
});
axios.post
The axios.post function sends data to the server. It requires the API URL, the data to be sent, and an optional configuration object.
Example:
const headers = {
Authorization: "Bearer <token>",
};
const body = {
name: "Sandy",
age: "25",
};
axios
.post("https://api.github.com/users/octocat", body, { headers })
.then((response) => {
console.log(response.data);
});
axios.delete
The axios.delete function deletes data on the server. It takes the API URL and an optional configuration object.
Example:
const headers = {
Authorization: "Bearer <token>",
};
axios
.delete("https://api.github.com/users/octocat", { headers })
.then((response) => {
console.log(response.data);
});
For more details on axios, refer to the official documentation.
Integrating the Form
To send form data to the server, we'll use axios.post. The following code sends the book object as JSON to the server and updates the UI with the new list of books.
const onSubmit = async (e) => {
e.preventDefault();
const book = {
name: e.target.name.value,
author: e.target.author.value,
price: parseInt(e.target.price.value),
stock: parseInt(e.target.stock.value),
};
axios
.post("http://localhost:2022/book", book)
.then(async () => {
const bookList = await axios.get("http://localhost:2022/book");
setBooks(bookList.data);
})
.catch(() => {
book._id = (books.length + 1).toString();
setBooks([...books, book]);
})
.finally(() => {
e.target.reset();
});
};
Integrating the Table
We'll use axios.get in the useEffect hook to fetch the list of books when the component mounts. Here’s how to do it:
React.useEffect(() => {
axios
.get("http://localhost:2022/book")
.then((response) => {
setBooks(response.data);
})
.catch(() => {
const defaultBook = {
_id: "1",
name: "Default
Book",
author: "Author",
price: 100,
stock: 10,
};
setBooks([defaultBook]);
});
}, []);
Integrating the Delete Button
We’ll use axios.delete to delete a book from the server. Here’s how to do it:
const deleteBook = async (id) => {
axios
.delete(`http://localhost:2022/book/${id}`)
.then(async () => {
const bookList = await axios.get("http://localhost:2022/book");
setBooks(bookList.data);
})
.catch(() => {
setBooks(books.filter((book) => book._id !== id));
});
};
Complete Code
Here is the complete code of the App.js file.
import "./App.css";
import axios from "axios";
import React from "react";
function App() {
/*
useState is a hook that allows us to create a state variable in our component and also update it when we want to.
It takes a single argument, the initial value of the state variable. We can then use this state variable to update the component.
We can also use this state variable to get the current value of the state variable.
*/
const [books, setBooks] = React.useState([]);
// useEffect is a hook that runs after the component is rendered, but before the component is updated.
React.useEffect(() => {
// This is to get the list of books from the backend.
axios
.get("http://localhost:2022/book")
.then((response) => {
// Once we get the list of books, we need to set the state of the component with the list of books.
setBooks(response.data);
})
.catch((error) => {
// If backend is not running, this will throw an error. So we need to handle it.
// I am adding book to the list, and it will be displayed in the UI.
const book = {
_id: (books.length + 1).toString(),
name: "Default Book",
author: "Default Author",
price: 0,
stock: 0,
};
// I am adding book to the list, and it will be displayed in the UI.
setBooks([book]);
});
}, []);
const onSubmit = async (e) => {
// e.preventDefault prevents the page from refreshing when the form is submitted (default behavior)
e.preventDefault();
// This is the body of the request, we can send it as a JSON object
const book = {
name: e.target.name.value,
author: e.target.author.value,
price: parseInt(e.target.price.value),
stock: parseInt(e.target.stock.value),
};
axios
.post("http://localhost:2022/book", book)
.then(async (res) => {
// Once the book is added, we need to get the list of books
const bookList = await axios.get("http://localhost:2022/book");
// And render the list of books in the UI. I am reassigning the state with the new list of books
setBooks(bookList.data);
})
.catch((err) => {
// If the backend is not running, this will throw an error. So we need to handle it.
// I am adding the book to the list, and it will be displayed in the UI.
book._id = (books.length + 1).toString();
const tempBooks = [...books, book];
setBooks(tempBooks);
})
.finally(() => {
// This is to clear the form after submitting it.
e.target.name.value = "";
e.target.author.value = "";
e.target.price.value = "";
e.target.stock.value = "";
});
};
const deleteBook = async (id) => {
// This is to delete the book from the list.
axios
.delete(`http://localhost:2022/book/${id}`)
.then(async (res) => {
// Once the book is deleted, we need to get the list of books
const bookList = await axios.get("http://localhost:2022/book");
// And render the list of books in the UI. I am reassigning the state with the new list of books
setBooks(bookList.data);
})
.catch((err) => {
// If the backend is not running, this will throw an error. So we need to handle it.
// I am deleting the book from the list, and it will be removed from the UI.
const tempBooks = books.filter((book) => book._id !== id);
setBooks(tempBooks);
});
};
return (
<div className="App">
<h1>Book Store</h1>
<h2>Add Book</h2>
<form onSubmit={onSubmit}>
<input type="text" name="name" placeholder="Name" />
<input type="text" name="author" placeholder="Author" />
<input type="number" name="price" placeholder="Price" />
<input type="number" name="stock" placeholder="Stock" />
<button type="submit">Save</button>
</form>
<h2>Books</h2>
<table>
<thead>
<tr>
<th>Book Name</th>
<th>Author Name</th>
<th>Price</th>
<th>Stock</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{books.map((book) => (
<tr>
<td>{book.name}</td>
<td>{book.author}</td>
<td>{book.price}</td>
<td>{book.stock}</td>
<td>
<button onClick={() => deleteBook(book._id)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default App;
Code
You can get the complete code from here