简体   繁体   中英

create component for form errors using react-hook-form

I'm using react-hook-form, and I'm wondering if it's possible to create a component that is passing the form errors using react-hook-form.

I have the following component

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>
      )}
    </>
  );
}

and I have added this to the following page

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>
    </>
  );
}

So I'm trying to create a component for all forms where I can simply just show any errors to keep the code DRY, but the component is not showing up.

Sure I'm missing something silly but I'm new to react so any help would be create.

You've have to use FormProvider in your Login component and useFormContext in your ErrorPopup

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

I've added a comment // where I've made changes.

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>
      )}
    </>
  );
}

I believe you need to use useFormContext instead of useForm in your ErrorsPopup component

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM