Updating a record means: load the current data into a form, let the user edit it, then send the changes back to the API.
The companion repo keeps update simpler and reuses UserCreate for PUT /api/users/{id} (full replacement). This lesson introduces a separate UserUpdate model with optional fields to teach the common “partial update shape” pattern.
Backend (Python)
API Client (JS)
Copy
class UserUpdate(BaseModel): name: str | None = None email: str | None = None@app.put("/api/users/{user_id}")def update_user(user_id: int, updates: UserUpdate): user = find_user(user_id) if not user: raise HTTPException(status_code=404, detail="User not found") if updates.name is not None: user["name"] = updates.name if updates.email is not None: user["email"] = updates.email return user
Copy
export async function updateUser(id, data) { const response = await fetch(`${API_URL}/api/users/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json();}
The state update uses .map() — replace the old user with the updated one, leave everything else unchanged.
prev.map(u => u.id === updatedUser.id ? updatedUser : u) is the standard pattern for updating one item in a list. It creates a new array where only the matching item is replaced. You’ll use this pattern constantly.
async function handleSave(formData) { setSaving(true); try { const updated = await updateUser(user.id, formData); // Wait for API onUpdate(updated); // Then update UI } catch (err) { setError(err.message); // Show error, UI unchanged } finally { setSaving(false); }}
Optimistic (update UI immediately, roll back on error):
Copy
async function handleSave(formData) { const previousUser = { ...user }; // Save backup // Update UI immediately (optimistic) onUpdate({ ...user, ...formData }); setEditing(false); try { await updateUser(user.id, formData); // Confirm with server } catch (err) { onUpdate(previousUser); // Roll back on failure setError("Save failed. Your changes were reverted."); setEditing(true); }}
Approach
UX
Complexity
When to use
Pessimistic
Spinner, then update
Simple
Most operations (start here)
Optimistic
Instant, may roll back
More complex
Frequent actions (likes, toggles)
Start with pessimistic updates. They’re simpler and always correct. Only switch to optimistic updates for actions where the slight delay feels sluggish — like toggling a checkbox or liking a post.