React CRUD Application
# React CRUD Application
1. Introduction
Every software application does four basic things: Create new data, Read existing data, Update existing data, and Delete data. This is famously known as the CRUD pattern. In this chapter, we will tie together everything we've learned—Components, State, Props, Forms, Effects, and API fetching—to build the foundational architecture of a modern web application.2. Learning Objectives
By the end of this chapter, you will be able to:- Combine React Forms with API POST requests to Create data.
-
Use
useEffectto Read data on component mount.
- Send API PUT/PATCH requests to Update data.
- Send API DELETE requests and update the UI to Delete data.
- Implement optimistic UI updates for a snappy user experience.
3. Beginner-Friendly Explanations
The Syncing Problem
When you click "Delete" on a task, two things must happen:- 1. The backend database must delete the row.
- 2. The frontend React UI must remove the item from the screen.
Pessimistic vs. Optimistic Updates
- Pessimistic: Click delete -> Show spinner -> Wait for server response -> Re-fetch all tasks to update UI. (Safe, but slow).
- Optimistic: Click delete -> Instantly remove item from React state (UI updates immediately) -> Send request to server in the background. If the server errors out, revert the UI back. (Feels incredibly fast).
4. Syntax Explanation (The Four API Verbs)
READ (GET Request)
We covered this in Chapter 19. It runs inside auseEffect.
```jsx id="ch26_ex1"
const fetchTasks = async () => {
const res = await fetch('/api/tasks');
const data = await res.json();
setTasks(data);
};
jsx id="ch26_ex2" const createTask = async (newTaskTitle) => { const res = await fetch('/api/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: newTaskTitle, completed: false }) }); const savedTask = await res.json(); // Update state to include the new task setTasks([...tasks, savedTask]); };
jsx id="ch26_ex3"
const toggleTask = async (id, currentStatus) => {
await fetch(/api/tasks/${id}, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ completed: !currentStatus })
});
// Update local state by mapping through and finding the changed item
setTasks(tasks.map(task =>
task.id === id ? { ...task, completed: !currentStatus } : task
));
};
jsx id="ch26_ex4"
const deleteTask = async (id) => {
await fetch(/api/tasks/${id}, {
method: 'DELETE'
});
// Update local state by filtering out the deleted item
setTasks(tasks.filter(task => task.id !== id));
};
jsx id="ch26_proj1" // Example React component import React, { useState } from 'react';
export default function TaskManager() { // 1. READ: Initial state acts as our database const [tasks, setTasks] = useState([ { id: 1, title: 'Learn React Hooks', completed: true }, { id: 2, title: 'Build a CRUD application', completed: false }, ]); const [newTaskInput, setNewTaskInput] = useState('');
// 2. CREATE const handleAddTask = (e) => { e.preventDefault(); if (!newTaskInput.trim()) return; const newTask = { id: Date.now(), // Generate a fake ID title: newTaskInput, completed: false }; setTasks([...tasks, newTask]); setNewTaskInput(''); // Clear form };
// 3. UPDATE const handleToggleComplete = (id) => { setTasks(tasks.map(task => task.id === id ? { ...task, completed: !task.completed } : task )); };
// 4. DELETE const handleDelete = (id) => { setTasks(tasks.filter(task => task.id !== id)); };
return ( <div className="min-h-screen bg-slate-100 p-8 flex justify-center"> <div className="w-full max-w-xl bg-white shadow-xl rounded-2xl p-8 border border-slate-200"> <h1 className="text-3xl font-black text-slate-800 mb-8">Task Manager</h1>
{/* CREATE Form */} <form onSubmit={handleAddTask} className="flex gap-2 mb-8"> <input type="text" value={newTaskInput} onChange={(e) => setNewTaskInput(e.target.value)} placeholder="What needs to be done?" className="flex-1 px-4 py-3 bg-slate-50 border border-slate-200 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none transition shadow-inner" /> <button type="submit" className="bg-blue-600 hover:bg-blue-700 text-white font-bold px-6 py-3 rounded-lg shadow transition transform hover:scale-105"> Add </button> </form>
{/* READ List */}
{tasks.length === 0 ? (
<p className="text-center text-slate-400 italic py-10">No tasks remaining. You're all caught up!</p>
) : (
<ul className="space-y-3">
{tasks.map(task => (
<li key={task.id} className="flex items-center justify-between p-4 bg-white border border-slate-200 rounded-xl hover:shadow-md transition">
{/* UPDATE Checkbox */}
<div className="flex items-center gap-4">
<input
type="checkbox"
checked={task.completed}
onChange={() => handleToggleComplete(task.id)}
className="w-5 h-5 text-blue-600 cursor-pointer"
/>
<span className={text-lg ${task.completed ? 'line-through text-slate-400' : 'text-slate-700 font-medium'}}>
{task.title}
</span>
</div>
{/* DELETE Button */}
<button
onClick={() => handleDelete(task.id)}
className="text-red-400 hover:text-red-600 hover:bg-red-50 p-2 rounded transition font-bold"
>
✕
</button>
</li>
))}
</ul>
)}
</div>
</div>
);
}
``
11. Coding Challenges
Challenge 1: Add an "Edit" feature to the Task Manager. When a user double-clicks a task's title, replace the <span> with an <input> field. When they hit enter, save the updated string to the task's title in state.
12. MCQs with Answers
Q1: Which HTTP method is conventionally used to UPDATE existing data?
A) GET
B) POST
C) PATCH / PUT
D) DELETE
*Answer: C*
Q2: What is the correct way to delete an item with
id: 5 from a state array?
A) items.splice(5, 1); setItems(items);
B) setItems(items.filter(item => item.id !== 5));
C) delete items[5];
*Answer: B*
13. Interview Questions
-
Q: What is the difference between Optimistic and Pessimistic UI updates?
-
Q: Explain how you ensure the UI stays in sync with the database when modifying data.
14. FAQs
Do I always have to manually update state after an API call?
If you use native fetch(), yes. However, if you use modern tools like React Query, it automatically re-fetches the list for you behind the scenes when you trigger a mutation, guaranteeing your UI is always perfectly synced with the database.
15. Summary
Mastering the CRUD flow is the final boss of React basics. By orchestrating forms, useEffect, state updates (via .map and .filter), and asynchronous fetch` requests, you are now capable of building fully functional, data-driven web applications.