使用 react-hook-form 为表单错误创建组件

create component for form errors using react-hook-form

我正在使用 react-hook-form,我想知道是否可以使用 react-hook-form 创建一个传递表单错误的组件。

我有以下组件

import { useForm } from "react-hook-form";
import { XIcon } from "@heroicons/react/outline";

export default function ErrorsPopup() {
  const {
    formState: { errors },
  } = useForm({
    criteriaMode: "all",
  });

  return (
    <>
      {Object.keys(errors).length > 0 && (
        <div className="mt-6 text-white bg-red-600 border-4 border-red-700 alert-start">
          <div className="flex justify-between items-center bg-red-700 py-2 px-4">
            <div>
              <p>The following errors have occurred:</p>
            </div>

            <div className="cursor-pointer">
              <XIcon className="h-6 w-6 text-white alert-close" />
            </div>
          </div>

          <div className="py-2 px-4">
            <div className="flex flex-col gap-2">
              {Object.entries(errors).map(([index, error]) => (
                <div className="flex flex-col gap-y-6 sm:gap-x-8" key={index}>
                  <p>&bull; {error.message}</p>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}
    </>
  );
}

我已将其添加到下一页

import { useState } from "react";
import Head from "next/head";
import Link from "next/link";
import Image from "next/image";
import Background from "../../../public/images/option1.png";
import Router from "next/router";
import { signIn } from "next-auth/client";
import { useForm } from "react-hook-form";
import { SaveIcon } from "@heroicons/react/outline";

import ErrorsPopup from "../../components/ErrorsPopup";

export default function Login() {
  const { register, handleSubmit } = useForm();

  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const onSubmit = async () => {
    await signIn("credentials", {
      redirect: false,
      email: email,
      password: password,
    });

    Router.push("/dashboard");
  };

  return (
    <>
      <Head>
        <title>Ellis Development - Login</title>
      </Head>

      <div className="relative">
        <div className="md:flex">
          {/* Image */}
          <div className="flex items-center justify-center bg-blue-700 h-screen lg:w-96">
            <Image src={Background} width={350} height={350} layout="fixed" />
          </div>

          {/* Contact form */}
          <div className="flex flex-col justify-center py-10 px-6 sm:px-10 w-full">
            <h1 className="text-4xl font-extrabold text-grey-800">Login</h1>

            {/* errors */}
            <ErrorsPopup />

            <form
              onSubmit={handleSubmit(onSubmit)}
              className="mt-6 flex flex-col gap-y-6 sm:gap-x-8"
            >
              {/* email field */}
              <div>
                <label
                  htmlFor="email"
                  className="block text-sm font-medium text-gray-900"
                >
                  Email
                </label>

                <div className="mt-1">
                  <input
                    {...register("email", { required: "E-mail is required" })}
                    type="text"
                    name="email"
                    id="email"
                    className="py-3 px-4 block w-full shadow-sm text-gray-900 focus:ring-blue-700 focus:border-blue-900 border-gray-300 rounded-md"
                    onChange={(event) => setEmail(event.target.value)}
                  />
                </div>
              </div>

              {/* password field */}
              <div>
                <label
                  htmlFor="password"
                  className="block text-sm font-medium text-gray-900"
                >
                  Password
                </label>

                <div className="mt-1">
                  <input
                    {...register("password", {
                      required: "Password is required",
                    })}
                    type="password"
                    name="password"
                    id="password"
                    className="py-3 px-4 block w-full shadow-sm text-gray-900 focus:ring-blue-700 focus:border-blue-900 border-gray-300 rounded-md"
                    onChange={(event) => setPassword(event.target.value)}
                  />
                </div>
              </div>

              <div className="flex items-center justify-between sm:col-span-2">
                <div>
                  <button
                    type="submit"
                    className="mt-2 mr-2 w-full inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-md shadow-sm text-base font-medium text-white bg-blue-700 hover:bg-blue-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-700 sm:w-auto"
                  >
                    <SaveIcon className="w-4 h-4 mr-4" />
                    Login
                  </button>
                </div>

                <div>
                  <Link href="/dashboard/auth/register">
                    <a className="underline decoration-blue-500 decoration-4 hover:decoration-2 mr-4">
                      Register
                    </a>
                  </Link>

                  <Link href="/dashboard/auth/forgot">
                    <a className="underline decoration-blue-500 decoration-4 hover:decoration-2">
                      Forgot your password?
                    </a>
                  </Link>
                </div>
              </div>
            </form>
          </div>
        </div>
      </div>
    </>
  );
}

所以我正在尝试为所有表单创建一个组件,我可以在其中简单地显示任何错误以保持代码干燥,但该组件没有显示。

当然,我遗漏了一些愚蠢的东西,但我是新手,所以可以提供任何帮助。

我认为您需要在 ErrorsPopup 组件中使用 useFormContext 而不是 useForm

https://react-hook-form.com/api/useformcontext

您必须在 Login 组件中使用 FormProvider 并在 ErrorPopup

中使用 useFormContext

https://react-hook-form.com/api/useformcontext

我添加了评论 // 我所做的更改。

Login.js

import { useState } from "react";
import Head from "next/head";
import Link from "next/link";
import Image from "next/image";
import Background from "../../../public/images/option1.png";
import Router from "next/router";
import { signIn } from "next-auth/client";
import { useForm, FormProvider } from "react-hook-form"; //
import { SaveIcon } from "@heroicons/react/outline";

import ErrorsPopup from "../../components/ErrorsPopup";

export default function Login() {
  const methods = useForm(); //
const { register, handleSubmit } = methods; //

  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const onSubmit = async () => {
    await signIn("credentials", {
      redirect: false,
      email: email,
      password: password,
    });

    Router.push("/dashboard");
  };

  return (
    <>
      <Head>
        <title>Ellis Development - Login</title>
      </Head>

      <div className="relative">
        <div className="md:flex">
          {/* Image */}
          <div className="flex items-center justify-center bg-blue-700 h-screen lg:w-96">
            <Image src={Background} width={350} height={350} layout="fixed" />
          </div>

          {/* Contact form */}
          <div className="flex flex-col justify-center py-10 px-6 sm:px-10 w-full">
            <h1 className="text-4xl font-extrabold text-grey-800">Login</h1>
        
           <FormProvider {...methods}>  //
            {/* errors */}
            <ErrorsPopup />

            <form
              onSubmit={handleSubmit(onSubmit)}
              className="mt-6 flex flex-col gap-y-6 sm:gap-x-8"
            >
              {/* email field */}
              <div>
                <label
                  htmlFor="email"
                  className="block text-sm font-medium text-gray-900"
                >
                  Email
                </label>

                <div className="mt-1">
                  <input
                    {...register("email", { required: "E-mail is required" })}
                    type="text"
                    name="email"
                    id="email"
                    className="py-3 px-4 block w-full shadow-sm text-gray-900 focus:ring-blue-700 focus:border-blue-900 border-gray-300 rounded-md"
                    onChange={(event) => setEmail(event.target.value)}
                  />
                </div>
              </div>

              {/* password field */}
              <div>
                <label
                  htmlFor="password"
                  className="block text-sm font-medium text-gray-900"
                >
                  Password
                </label>

                <div className="mt-1">
                  <input
                    {...register("password", {
                      required: "Password is required",
                    })}
                    type="password"
                    name="password"
                    id="password"
                    className="py-3 px-4 block w-full shadow-sm text-gray-900 focus:ring-blue-700 focus:border-blue-900 border-gray-300 rounded-md"
                    onChange={(event) => setPassword(event.target.value)}
                  />
                </div>
              </div>

              <div className="flex items-center justify-between sm:col-span-2">
                <div>
                  <button
                    type="submit"
                    className="mt-2 mr-2 w-full inline-flex items-center justify-center px-6 py-3 border border-transparent rounded-md shadow-sm text-base font-medium text-white bg-blue-700 hover:bg-blue-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-700 sm:w-auto"
                  >
                    <SaveIcon className="w-4 h-4 mr-4" />
                    Login
                  </button>
                </div>

                <div>
                  <Link href="/dashboard/auth/register">
                    <a className="underline decoration-blue-500 decoration-4 hover:decoration-2 mr-4">
                      Register
                    </a>
                  </Link>

                  <Link href="/dashboard/auth/forgot">
                    <a className="underline decoration-blue-500 decoration-4 hover:decoration-2">
                      Forgot your password?
                    </a>
                  </Link>
                </div>
              </div>
            </form>
</FormProvider> //
          </div>
        </div>
      </div>
    </>
  );
}

ErrorPopup.js

import { useFormContext } from "react-hook-form"; // 
import { XIcon } from "@heroicons/react/outline";

export default function ErrorsPopup() {
  const {
    formState: { errors },
  } = useFormContext({ // 
    criteriaMode: "all",
  });

  return (
    <>
      {Object.keys(errors).length > 0 && (
        <div className="mt-6 text-white bg-red-600 border-4 border-red-700 alert-start">
          <div className="flex justify-between items-center bg-red-700 py-2 px-4">
            <div>
              <p>The following errors have occurred:</p>
            </div>

            <div className="cursor-pointer">
              <XIcon className="h-6 w-6 text-white alert-close" />
            </div>
          </div>

          <div className="py-2 px-4">
            <div className="flex flex-col gap-2">
              {Object.entries(errors).map(([index, error]) => (
                <div className="flex flex-col gap-y-6 sm:gap-x-8" key={index}>
                  <p>&bull; {error.message}</p>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}
    </>
  );
}