reactjsvalidationzod

Zod Regex Expression not working as expected


Hey everyone I have the following code below and I am having trouble getting the error of 'Invalid domain structure' to show up in the input error property. I simply get an object that looks like this even when theres text in the field I want to get domain is invalid. To be clear this isn't an email I am validating I wanna validate specifically a domain. Thanks enter image description here

import Button from '@/components/Button/Button';
import { useCallback, useMemo } from 'react';
import { useForm, SubmitHandler, FieldError } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { twMerge } from 'tailwind-merge';
import { useLogin } from '@/services/auth/hooks/useLogin';

const schema = z.object({
  vendor: z
    .string()
    .min(1, { message: 'Vendor is required' }) // Custom required message
    .regex(/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, { message: 'Invalid domain structure' }),
});
interface FormData {
  vendor: string;
}

interface InputProps extends LabelInputProps {
  placeHolder: string;
}

const Input = ({ id, placeHolder, networkError, inputError, type }: InputProps) => {
  return (
    <div className="relative mt-2">
      <input
        id={id}
        type={type}
        placeholder={placeHolder}
        className={twMerge(
          `w-full border-[1px] pl-3 pr-${networkError ? '10' : '4'} py-2 text-sm rounded-sm`,
          networkError || inputError ? 'border-error-color focus:border-error-color focus:ring-error-color' : 'border-gray-300'
        )}
      />
      {inputError && <p className="text-sm text-red-500">{inputError.message}</p>}
    </div>
  );
};

const Label = ({ htmlFor, name }: { htmlFor: string; name: string }) => (
  <label htmlFor={htmlFor} className="w-full text-sm font-bold flex">
    {name} <div className="text-red-500">*</div>
  </label>
);

const upperCaseFirstLetter = (name: string) => name.charAt(0).toUpperCase() + name.slice(1);

interface LabelInputProps {
  id: string;
  type?: 'text';
  networkError?: Error | null;
  inputError?: FieldError;
}

const LabeledInput = ({ id, type, networkError, inputError }: LabelInputProps) => {
  const name = useMemo(() => upperCaseFirstLetter(id), [id]);
  return (
    <>
      <Label htmlFor={id} name={name} />
      <Input id={id} type={type} placeHolder={name} networkError={networkError} inputError={inputError} />
    </>
  );
};

const SignInText = () => <div className="text-sm font-normal text-gray-600">Enter your vendor email</div>;

const Form = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormData>({ resolver: zodResolver(schema) });

  const onSubmit: SubmitHandler<FormData> = useCallback((formData: FormData) => {
    console.log(formData);
    // loginHandler({ type: AuthenticationType.AwsCognito, navigate, user });
    // gonna write my a vender handler
  }, []);

  const { loginHandler, error: networkError } = useLogin();

  return (
    <div className="w-1/4 mt-4 mx-auto p-6 bg-white rounded-lg shadow-md border border-gray-200">
      {networkError && <p className="text-red-500 text-sm">{networkError.message}</p>}
      <SignInText />
      <form className="mt-4" onSubmit={handleSubmit(onSubmit)}>
        <LabeledInput id="vendor" type="text" networkError={networkError} inputError={errors.vendor} />

        <Button className="mt-4 w-full bg-blue-500 text-white hover:bg-blue-600 rounded-md py-2">Submit</Button>
      </form>
    </div>
  );
};

export default Form;

Solution

  • Looks like like you're not registering the field under your form; rather, you're just passing an associated id to the input: https://www.react-hook-form.com/api/useform/register/. For a more controlled wrapper component, you may also leverage Controller or useController: https://www.react-hook-form.com/api/usecontroller/controller/.

    Your regex seems to resolve as you've described.