Replies: 3 comments
|
I think the simplest approach is to keep the input value nullable in the form state, but handle the validation separately in the schema. For example, you can allow const schema = z.object({
value: z
.number()
.min(1)
.max(5)
.nullable(),
});
onChange={(e) => {
const value = e.target.value;
field.onChange(value === "" ? null : Number(value));
}} |
|
Your third schema, Here is a complete native-input example expanding on the earlier Controller suggestion: import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const schema = z.object({ scientific_merit_score: z.number().min(1).max(5).nullable() });
export function ScoreForm() {
const {control,handleSubmit}=useForm<z.infer<typeof schema>>({resolver:zodResolver(schema),defaultValues:{scientific_merit_score:null}});
return <form noValidate onSubmit={handleSubmit(console.log)}>
<Controller name="scientific_merit_score" control={control} render={({field,fieldState})=><>
<label htmlFor="score">Scientific merit score</label>
<input id="score" type="number" min={1} max={5} step="any" name={field.name} ref={field.ref} onBlur={field.onBlur} value={field.value ?? ""} onChange={e=>field.onChange(e.target.value===""?null:e.target.valueAsNumber)} aria-invalid={!!fieldState.error} aria-describedby={fieldState.error?"score-error":undefined}/>
{fieldState.error && <p id="score-error">{fieldState.error.message}</p>}
</>}/><button type="submit">Save</button></form>;
}
This example passed TypeScript checks; schema checks accepted Prepared and checked with AI assistance. |
|
I think your schema is already close to correct. The important distinction is between an empty HTML input, which produces I'd handle this at the input boundary and ensure the default value is actually const schema = z.object({
scientific_merit_score: z
.number()
.min(1, "Minimum score is 1")
.max(5, "Maximum score is 5")
.nullable(),
});
type FormValues = z.infer<typeof schema>;
const { register, handleSubmit } = useForm<FormValues>({
resolver: zodResolver(schema),
defaultValues: {
scientific_merit_score: null,
},
});Then register the input with a transformation: <input
type="number"
min={1}
max={5}
{...register("scientific_merit_score", {
setValueAs: value =>
value === "" ? null : Number(value),
})}
/>The expected behavior is:
One thing I'd double-check in your original example: if Also, avoid using If you're using a controlled UI component with Hope that helps clarify where the conversion needs to happen! |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
I am ready to give up trying with zod and react hook form.
I would like to have a nullable input field in my form. if an entry is made, then it must be a number between 1-5. Null values are fine because the field is nullable.
ATTEMPT 1:
scientific_merit_score: z.number()
.nullable(),
// .refine((val) => val === null || (val >= 1 && val <= 5), {
// message: 'Score must be between 1 and 5',
// }),
ATTEMPT 2:
.refine(value => value === null || value >= 1, {
message: 'Number must be greater than or equal to 1',
})
.refine(value => value === null || value <= 5, {
message: 'Number must be less than or equal to 5',
}),
ATTEMPT 3:
z.number().min(1).max(5).nullable(),
<Input
type="number"
min={1}
max={5}
{...register(name, {
valueAsNumber: false,
setValueAs: v => v === '' ? null : Number(v)
})}
data-invalid={touchedFields[name] && !!errors[name] ? 'true' : undefined}
/>
{errors[name] && (
{errors[name]?.message}
)}
I have given up trying to use zod schema validation to check that it is a number between 1-5. None of the 3 attempts I have made above work.
Can anyone offer suggestions on what else I might try to allow null values in an input field -or alternatively check if the number is in the range?
I think part of the issue is that the form imposes a default value of 0. I've tried handling that and treating it is null, but it doesn't help to solve this.
<Input
type="number"
min={1}
max={5}
{...register(name, {
valueAsNumber: false,
setValueAs: v => v === 0 ? null : Number(v)
})}
data-invalid={touchedFields[name] && !!errors[name] ? 'true' : undefined}
/>
All reactions