Search code examples
react-hook-formtsx

React Hook Form at Least One TextField Has Value


I have 2 controlled inputs on a form, and I only need to validate that 1 of them is filled in. I'm having trouble with the validation logic.

                   function atLeastOneChecked(inputs: string) {
                       return inputs.length > 0;
                   }

                   <Controller
                        control={control}
                        {...register("phoneNumber", {
                            validate: atLeastOneChecked,
                        })}
                        render={({ field, fieldState: { error } }) => (
                            <TextField
                                {...field}
                                label="Phone Number"
                                error={!!error}
                                helperText={!!error && 'Phone or email required.'}
                            />
                        )}
                    />
                    <Controller
                        control={control}
                        {...register("email", {
                            validate: atLeastOneMediumChecked,
                        })}
                        render={({ field, fieldState: { error } }) => (
                            <TextField
                                {...field}
                                label="Email"
                                error={!!error}
                                helperText={!!error && 'Phone or email required.'}
                            />
                        )}
                    />

When I submit, the validation function gets called twice, once for each component. So it works the same as required: true on each one.

What I need is for the validate function to check both at once, and only return an error if both are empty.

I have tried:

                   function atLeastOneChecked(inputs: Inputs) {
                       

and

                   function atLeastOneChecked(inputs: string[]) {

But then the validate property throws an error that the types are incompatible.


Solution

  • The answer to this was to use a resolver:

    const { handleSubmit, control, register, formState: { errors } } = useForm<Inputs>({
            defaultValues: {
                ...defaultValues,
            },
            resolver: data => {
                const error: FieldErrors = {} as FieldErrors;
    
                if (!data.phoneNumber.trim() && !data.email.trim()) {
                    const message = 'Either Phone Number or Email is required.';
                    error.phoneNumberParam = error.emailParam = { message: message, type: '' };
                }
    
                return {
                    values: data,
                    errors: error,
                };
            },
        });