首頁  >  文章  >  web前端  >  使用react-form、zod、react data grid、react-query和json-server建立CRUD應用程式

使用react-form、zod、react data grid、react-query和json-server建立CRUD應用程式

Linda Hamilton
Linda Hamilton原創
2024-11-24 19:14:10548瀏覽

目標 :

我們的目標是開發一個 React CRUD 應用程式。

Building CRUD App with react-form, zod, react data grid, react-query and json-server

我們的堆疊:

  • 反應形式
  • 佐德
  • ag-grid-react
  • 反應查詢
  • json-伺服器

設定環境:

使用 vite 建立一個 React 專案:

npm create vite@latest crud-react -- --template react-ts

安裝依賴項:

npm install react-hook-form zod @hookform/resolvers ag-grid-react react-query axios

建立並啟動伺服器:

物件(產品)結構:

{
    "id": "w38y",
    "name": "Vitamin C Tablets",
    "price": 19.99,
    "expiryDate": "2025-01-01",
    "emailSupplier": "contact@healthplus.com"
}

在 /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"
    }
  ]
}

啟動 json-server :

npx json-server db/db.json

設定反應查詢:

更新 /src/App.tsx :

import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
    </QueryClientProvider>
  );
}

export default App;

建立 /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;
};

建立 /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] });
    },
  });
};

建立表格:

更新/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;
};

建立 /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;

建立 /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;

建立表:

建立 /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;

Git 儲存庫:

https://github.com/TesMae/crud-react

感謝您的關注!

以上是使用react-form、zod、react data grid、react-query和json-server建立CRUD應用程式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn