Careeroza
Creating Products from a Form
Form with Server-Side Errors
The form posts JSON and shows the field errors from your ProblemDetail response (the errors map from the Validation module) next to each input.
// src/pages/AddProduct.jsx
import { useState } from 'react';
import { createProduct } from '../api/products';
const empty = { sku: '', name: '', description: '', price: '', stock: 0, category: '' };
export default function AddProduct({ onCreated }) {
const [form, setForm] = useState(empty);
const [errors, setErrors] = useState({});
const change = (e) => setForm({ ...form, [e.target.name]: e.target.value });
async function submit(e) {
e.preventDefault();
try {
const saved = await createProduct({ ...form, price: Number(form.price), stock: Number(form.stock) });
setForm(empty);
setErrors({});
onCreated(saved);
} catch (problem) {
setErrors(problem.errors || { form: problem.detail });
}
}
return (
<form onSubmit={submit}>
{['sku', 'name', 'description', 'price', 'stock', 'category'].map((field) => (
<label key={field}>
{field}
<input name={field} value={form[field]} onChange={change} />
{errors[field] && <small className="error">{errors[field]}</small>}
</label>
))}
{errors.form && <p className="error">{errors.form}</p>}
<button type="submit">Add product</button>
</form>
);
}What Travels over the Wire
Example
Seen in the browser Network tab when the user leaves the name empty:
POST http://localhost:8080/api/products
Origin: http://localhost:5173
Content-Type: application/json
{"sku":"ELE-1003","name":"","description":"","price":0,"stock":10,"category":"Electronics"}Example output
HTTP/1.1 400 Bad Request
Access-Control-Allow-Origin: http://localhost:5173
Content-Type: application/problem+json
{"title":"Validation failed","status":400,"detail":"Request has invalid fields","errors":{"name":"must not be blank","price":"must be greater than or equal to 0.01"}}Exercise: After a successful create, the list page should show the new product without reloading. How do you connect AddProduct and ProductList?
Show answer
// Lift the products state to a common parent and pass a callback down:
function ProductsPage() {
const [products, setProducts] = useState([]);
// ...load with getProducts() in useEffect...
return (
<>
<AddProduct onCreated={(p) => setProducts((list) => [...list, p])} />
<ProductTable products={products} />
</>
);
}