APIs & State Management
Introduction to APIs in React
Key Points:
● API = Application Programming Interface
● In web development, APIs often return JSON data
● React can use APIs to fetch data from servers
● Two common ways:
1. Fetch API (built into browsers)
2. Axios (third-party library, easier syntax, better error handling)
Project setup (one-time)
npx create-react-app react-api-demo
cd react-api-demo
npm install axios
npm start
File structure (recommended)
src/
├── index.js
├── App.js
├── index.css
├── services/
│ ├── api.js # axios instance
│ └── userService.js # functions: getUsers, addUser, updateUser, deleteUser
└── components/
├── UsersList.js
└── UserForm.js
1) src/index.js
(Wraps the app — default Create React App entry)
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
2) src/services/api.js
(Axios instance — change baseURL to your API)
import axios from "axios";
const api = axios.create({
baseURL: "https://jsonplaceholder.typicode.com", // change to your backend
headers: {
"Content-Type": "application/json",
},
});
export default api;
3) src/services/userService.js
(Service functions wrapping API calls)
import api from "./api";
export const getUsers = () => api.get("/users");
export const addUser = (user) => api.post("/users", user);
export const updateUser = (id, user) => api.put(`/users/${id}`, user);
export const deleteUser = (id) => api.delete(`/users/${id}`);
Note: jsonplaceholder.typicode.com accepts POST/PUT/DELETE but does not
persist changes — it's good for demos.
4) src/App.js
(App-level state & handlers — lifts state, passes to components)
import React, { useEffect, useState } from "react";
import { getUsers, addUser as addUserApi, updateUser as updateUserApi, deleteUser as
deleteUserApi } from "./services/userService";
import UsersList from "./components/UsersList";
import UserForm from "./components/UserForm";
function App() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [editingUser, setEditingUser] = useState(null);
useEffect(() => {
fetchUsers();
}, []);
async function fetchUsers() {
try {
setLoading(true);
const res = await getUsers();
setUsers(res.data);
} catch (err) {
setError(err.message || "Error loading users");
} finally {
setLoading(false);
}
}
async function handleAddUser(userData) {
try {
const res = await addUserApi(userData);
// add created user to UI (jsonplaceholder returns created object)
setUsers(prev => [res.data, ...prev]);
} catch (err) {
setError(err.message);
}
}
async function handleUpdateUser(id, userData) {
try {
const res = await updateUserApi(id, userData);
setUsers(prev => prev.map(u => (u.id === id ? res.data : u)));
setEditingUser(null);
} catch (err) {
setError(err.message);
}
}
async function handleDeleteUser(id) {
try {
await deleteUserApi(id);
setUsers(prev => prev.filter(u => u.id !== id));
} catch (err) {
setError(err.message);
}
}
return (
<div style={{ padding: 20 }}>
<h1>React API CRUD Demo</h1>
<UserForm
onAdd={handleAddUser}
editingUser={editingUser}
onUpdate={handleUpdateUser}
onCancel={() => setEditingUser(null)}
/>
{loading ? (
<p>Loading users...</p>
) : error ? (
<p style={{ color: "red" }}>{error}</p>
):(
<UsersList users={users} onEdit={setEditingUser} onDelete={handleDeleteUser} />
)}
</div>
);
}
export default App;
5) src/components/UserForm.js
(Form used for Add and Edit)
import React, { useEffect, useState } from "react";
export default function UserForm({ onAdd, editingUser, onUpdate, onCancel }) {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
useEffect(() => {
if (editingUser) {
setName(editingUser.name || "");
setEmail(editingUser.email || "");
} else {
setName("");
setEmail("");
}
}, [editingUser]);
function handleSubmit(e) {
e.preventDefault();
const payload = { name, email };
if (editingUser) {
onUpdate(editingUser.id, payload);
} else {
onAdd(payload);
}
setName("");
setEmail("");
}
return (
<form onSubmit={handleSubmit} style={{ marginBottom: 16 }}>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
required
style={{ marginRight: 8 }}
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
style={{ marginRight: 8 }}
/>
<button type="submit">{editingUser ? "Update" : "Add"}</button>
{editingUser && (
<button type="button" onClick={onCancel} style={{ marginLeft: 8 }}>
Cancel
</button>
)}
</form>
);
}
6) src/components/UsersList.js
(Display + Edit/Delete actions)
import React from "react";
export default function UsersList({ users = [], onEdit, onDelete }) {
return (
<table border="1" cellPadding="8" style={{ width: "100%", borderCollapse: "collapse" }}>
<thead>
<tr>
<th style={{ textAlign: "left" }}>ID</th>
<th style={{ textAlign: "left" }}>Name</th>
<th style={{ textAlign: "left" }}>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{users.map((u) => (
<tr key={u.id}>
<td>{u.id}</td>
<td>{u.name}</td>
<td>{u.email}</td>
<td>
<button onClick={() => onEdit(u)} style={{ marginRight: 8 }}>Edit</button>
<button onClick={() => onDelete(u.id)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
);
}
Optional: Local fake API with json-server
1. Create db.json:
{
"users": [
{ "id": 1, "name": "Alice", "email": "[email protected]" },
{ "id": 2, "name": "Bob", "email": "[email protected]" }
]
}
2. Start server:
json-server --watch db.json --port 4000
3. Change api.js baseURL to http://localhost:4000.
CORS & common gotchas
● If your backend is on a different domain, ensure it allows CORS (backend must set
headers) or use a proxy during development.
● jsonplaceholder simulates success but won't persist PUT/POST for real persistence.
● Always handle errors and show loading states to users.
Quick testing checklist
1. npm start and open http://localhost:3000
2. The app fetches users (GET) and displays them.
3. Use the form to add a user (POST) — see it added in UI.
4. Click Edit → update → the row should update (PUT).
5. Delete a user → it should disappear (DELETE).