I have a form where I am validating the types using zod:
const formSchema = z.object({
numberField: z.number().min(1, "numberField is required"),
});
That looks like:
<form onSubmit={handleFormOnSubmit} className="space-y-4">
<div className="xs:col-span-full sm:col-span-3">
<NumberField
name="numberField"
label="Insert a number"
control={control}
/>
</div>
</form>
The NumberField I use looks like this:
import React from "react";
import { useController } from "react-hook-form";
import type { FieldValues, UseControllerProps } from "react-hook-form";
import NumberInput from "../../Inputs/NumberInput";
import type { NumberInputProps } from "../../Inputs/NumberInput";
export type NumberFieldProps<P extends FieldValues> = UseControllerProps<P> &
Omit<NumberInputProps, "name">;
const NumberField = <P extends FieldValues>({
name,
control,
defaultValue,
...rest
}: NumberFieldProps<P>) => {
const {
field: { onChange, onBlur, value, name: fieldName },
fieldState: { error },
} = useController({ name, control, defaultValue });
return (
<NumberInput
name={fieldName}
onChange={onChange}
onBlur={onBlur}
value={value as number}
error={error?.message}
{...rest}
/>
);
};
export default NumberField;
And my NumberInput like this:
import React from "react";
import clsx from "clsx";
import type { InputHTMLAttributes } from "react";
export interface NumberInputProps
extends InputHTMLAttributes<HTMLInputElement> {
name: string;
label: string;
error?: string;
helperText?: string;
}
const NumberInput: React.FC<NumberInputProps> = ({
label,
error,
helperText,
className = "",
name,
required,
value,
...inputProps
}) => {
const inputClassName = clsx(
"bg-gray-800 block border-0 flex-1 focus:ring-2 focus:ring-indigo-600 focus:ring-inset placeholder:text-gray-400 py-1.5 ring-1 ring-inset rounded-md shadow-sm sm:leading-6 sm:text-sm text-white w-full",
{
"ring-red-500": error,
"ring-gray-700": !error,
},
className,
);
return (
<div className="w-full">
<label
htmlFor={name}
className="block text-sm font-medium leading-6 text-white"
>
{label} {required && <span className="text-red-500">*</span>}
</label>
<div className="mt-2">
<input
type="number"
name={name}
id={name}
className={inputClassName}
value={Number(value)}
{...inputProps}
/>
</div>
{error && <p className="mt-1 text-sm text-red-500">{error}</p>}
{helperText && <p className="mt-1 text-sm text-gray-400">{helperText}</p>}
</div>
);
};
export default NumberInput;
Somehow I am getting 'Expected number, received string' as a validation error, even if I can't put any string value in the form. Any advice of what I could be doing wrong or what I would need to do to fix it?
Many thanks in advance!
Setting type=number
does not parse the output value. Neither value=Number(value)
. You have to parse it manually.
I am not sure if it already has any provided methods to do that, like the one valueAsNumber
, but you can always add that logic to the onChange of the controller
// NumberInput
<input
...
{...inputProps}
onChange={(event) => inputProps.onChange(Number(event.target.value))}
/>