Matlamat kami adalah untuk membangunkan aplikasi CRUD bertindak balas.
Timbunan kami :
Buat projek reaksi menggunakan vite :
npm create vite@latest crud-react -- --template react-ts
Pasang kebergantungan :
npm install react-hook-form zod @hookform/resolvers ag-grid-react react-query axios
Struktur objek (produk) :
{ "id": "w38y", "name": "Vitamin C Tablets", "price": 19.99, "expiryDate": "2025-01-01", "emailSupplier": "contact@healthplus.com" }
Buat fail yang mengandungi data sampel dalam /db/db.json :
{ "products": [ { "id": "w38y", "name": "Vitamin C Tablets", "price": 19.99, "expiryDate": "2025-01-01", "emailSupplier": "contact@healthplus.com" }, { "id": "a99x", "name": "Omega-3 Fish Oil", "price": 30.99, "expiryDate": "2024-11-15", "emailSupplier": "support@nutricore.com" }, { "id": "x82j", "name": "Calcium + Vitamin D", "price": 15.5, "expiryDate": "2026-06-01", "emailSupplier": "orders@welllifelabs.com" }, { "id": "a40i", "name": "Zinc Lozenges", "price": 12.99, "expiryDate": "2024-09-30", "emailSupplier": "sales@herbalessentials.com" }, { "id": "c52f", "name": "Probiotic Capsules", "price": 25.75, "expiryDate": "2025-03-20", "emailSupplier": "info@guthealthlabs.com" } ] }
Mulakan json-server :
npx json-server db/db.json
Kemas kini /src/App.tsx :
import { QueryClient, QueryClientProvider } from "react-query"; const queryClient = new QueryClient(); function App() { return ( <QueryClientProvider client={queryClient}> </QueryClientProvider> ); } export default App;
Buat /src/types.ts :
export type FormData = { name: string; price: number; expiryDate: string; emailSupplier: string; }; export type FormFieldNames = "name" | "price" | "expiryDate" | "emailSupplier"; export type Product = { id: string; name: string; price: number; expiryDate: string; emailSupplier: string; };
Buat /src/server/productQuery.ts :
import { useMutation, useQuery, useQueryClient } from "react-query"; import { FormData, Product } from "../types"; import axios from "axios"; const URL = "http://localhost:3000"; const PRODUCTS = "products"; export const save = async (product: FormData) => axios.post(`${URL}/${PRODUCTS}`, product); export const useSave = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (newProduct: FormData) => save(newProduct), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [PRODUCTS] }); }, }); }; export const fetch = async () => { const result = await axios.get(`${URL}/${PRODUCTS}`); return result.data; }; export const useProducts = () => useQuery<Product[]>({ queryKey: [PRODUCTS], queryFn: fetch, }); const remove = async (id: string) => { await axios.delete(`${URL}/${PRODUCTS}/${id}`); }; export const useRemove = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: string) => remove(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [PRODUCTS] }); }, }); }; export const update = async (product: Product) => axios.put(`${URL}/${PRODUCTS}/${product.id}`, product); export const useUpdate = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (product: Product) => update(product), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [PRODUCTS] }); }, }); };
Kemas kini /src/types.ts:
import { z, ZodType } from "zod"; export type FormData = { name: string; price: number; expiryDate: string; emailSupplier: string; }; export type FormFieldNames = "name" | "price" | "expiryDate" | "emailSupplier"; export const ProductSchema: ZodType<FormData> = z.object({ name: z.string().min(3), price: z.number().min(1).max(1000), expiryDate: z .string() .refine( (date) => new Date(date) > new Date(), "Expiry Date must be superior than current date", ), emailSupplier: z.string().email(), }); export type Product = { id: string; name: string; price: number; expiryDate: string; emailSupplier: string; };
Buat /src/components/form/FormField.tsx :
import { FieldError, UseFormRegister } from "react-hook-form"; import { FormData, FormFieldNames } from "../../types"; type FormFieldProps = { type: string; placeholder: string; name: FormFieldNames; register: UseFormRegister<FormData>; error: FieldError | undefined; valueAsNumber?: boolean; step?: number | string; }; const FormField = ({ type, placeholder, name, register, error, valueAsNumber, step, }: FormFieldProps) => ( <> <input type={type} placeholder={placeholder} step={step} {...register(name, { valueAsNumber })} /> {error && <span> {error.message} </span>} </> ); export default FormField;
Buat /src/components/form/Form.tsx :
import { useForm } from "react-hook-form"; import FormField from "./FormField"; import { FormData, ProductSchema } from "../../types"; import { zodResolver } from "@hookform/resolvers/zod"; import { useSave } from "../../server/productQuery"; const Form = () => { const { register, handleSubmit, formState: { errors }, } = useForm<FormData>({ resolver: zodResolver(ProductSchema), }); const mutation = useSave(); const onSubmit = (data: FormData) => { mutation.mutate(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <div> <FormField type="text" placeholder="Name" name="name" register={register} error={errors.name} /> <FormField type="number" placeholder="Price" name="price" step="0.01" register={register} error={errors.price} valueAsNumber /> <FormField type="date" placeholder="Expiry Date" name="expiryDate" register={register} error={errors.expiryDate} /> <FormField type="email" placeholder="Email" name="emailSupplier" register={register} error={errors.emailSupplier} /> <button type="submit">Add</button> </div> </form> ); }; export default Form;
Buat /src/components/table/Products.tsx :
import { useMemo } from "react"; import { Product } from "../../types"; import { useProducts, useRemove, useUpdate } from "../../server/productQuery"; import { AgGridReact } from "ag-grid-react"; import { ColDef, ColGroupDef } from "ag-grid-community"; import "ag-grid-community/styles/ag-grid.css"; import "ag-grid-community/styles/ag-theme-quartz.css"; const Products = () => { const { data: products } = useProducts(); const removeMutation = useRemove(); const updateMutation = useUpdate(); const columns = useMemo<(ColDef | ColGroupDef<Product>)[]>( () => [ { field: "id", editable: false }, { field: "name", editable: true }, { field: "price", editable: true }, { field: "expiryDate", editable: true }, { field: "emailSupplier", editable: true }, { field: "delete", sortable: false, editable: false, cellRenderer: (params: { data: Product }) => ( <button onClick={() => removeMutation.mutate(params.data.id)}> Delete </button> ), }, ], [], ); return ( <div className="ag-theme-quartz"> <p>Update /src/App.tsx :<br> </p> <pre class="brush:php;toolbar:false">import { QueryClient, QueryClientProvider } from "react-query"; import Form from "./components/form/Form"; import Products from "./components/table/Products"; const queryClient = new QueryClient(); function App() { return ( <QueryClientProvider client={queryClient}> <Form /> <Products /> </QueryClientProvider> ); } export default App;
https://github.com/TesMae/crud-react
Atas ialah kandungan terperinci Membina Apl CRUD dengan bentuk tindak balas, zod, grid data tindak balas, pertanyaan tindak balas dan pelayan json. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!