Combined Approach with Unified Schema and Conditional Handling

Approach Overview:

  • Single Schema: Use a single schema with a conditional image validation based on whether the form is in “add” or “edit” mode.
  • Unified Form Logic: Simplify the form logic by handling image previews within the form state, while keeping the option to handle image conversion (if needed).
const { data, isFetching } = useQuery(
import React from "react";
import { z } from "zod";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useState } from "react";

const productFormSchema = z.object({
  name: z.string().min(1, { message: "Name is required" }),
 image: z
    .union([
      z.instanceof(File, { message: "Image is required" }),
      z.string().optional(), // Allow the existing image URL for editing mode
    ])
    .refine((value) => value instanceof File || typeof value === "string", {
      message: "Image is required",
    }),
});
export type ProductFormValues = z.infer<typeof productFormSchema>;
interface ProductForm2Props { product?: Product; }

export const ProductForm1 = ({ product }: ProductForm2Props) => {
  const isAddMode = !product;
  const [imagePreview, setImagePreview] = useState<string | null>(
    product?.image ?? null
  );

  const {
    register,
    handleSubmit,
    control,
    watch,
    reset,
    formState: { errors, isSubmitting, isDirty },
  } = useForm<ProductFormValues>({
    resolver: zodResolver(productFormSchema),
    defaultValues: {
      name: product?.name ?? "",
      image: product?.image ?? "", // Use the existing image URL for editing mode
    },
  });

  const image = watch("image");
  useEffect(() => {
    if (image instanceof File) {
      const imageUrl = URL.createObjectURL(image);
      setImagePreview(imageUrl);
 return () => URL.revokeObjectURL(imageUrl);
    }
    if (typeof image === "string") {
      setImagePreview(image);
    }
  }, [image]);

  const onSubmitHandler = async (data: ProductFormValues) => {
    console.log(data);

    let imageUrl: string | undefined;
    if (data.image instanceof File) {
      // build FormData for uploading image
      const formData = new FormData();
      formData.append("file", data.image);

      // mock upload image to server to get image url
      imageUrl = await new Promise<string>((resolve) => {
        setTimeout(() => {
          resolve("https://via.placeholder.com/150");
        }, 1000);
      });
    } else {
      imageUrl = data.image; // Use the existing image URL for updating mode
    }
    if (isAddMode) {
      // create product
      console.log({ ...data, image: imageUrl! });
    } else {
      // update product
      console.log({ id: product!.id, ...data, image: imageUrl });
    }
    reset();
  };
  return (
    <form onSubmit={handleSubmit(onSubmitHandler)}>
      <input {...register("name")} />
    {errors.name && <span>{errors.name.message}</span>}
      <Controller
        name="image"
        control={control}
        render={({ field: { ref, name, onBlur, onChange } }) => (
          <input
            type="file"
            ref={ref}
            name={name}
            onBlur={onBlur}
            onChange={(e) => {
              const file = e.target.files?.[0];
              onChange(file ? file : imagePreview); // Keep the existing image in edit mode
              setImagePreview(
                file ? URL.createObjectURL(file) : product?.image ?? null
              );
            }}
          />
        )}
      />
      {imagePreview && <img src={imagePreview} alt="preview" />}
      {errors.image && <span>{errors.image.message}</span>}

      <button type="submit" disabled={(!isAddMode && !isDirty) || isSubmitting}>
        {isSubmitting ? "Submitting..." : "Submit"}
      </button>
    </form>
  );
};

This approach should simplify your codebase while retaining flexibility for future enhancements.

Need Help With React Development?

Work with our skilled React developers to accelerate your project and boost its performance.

Hire Reactjs Developers

Support On Demand!

Related Q&A