I am currently working on implementing a React-based Typescript Dynamic form that allows users to input two fields [{fileName: string, file: File}]
. Users should be able to add multiple sets of these fields [{fileName: string, file: File}]
upon successful ZOD validation. Below you can find the relevant code snippets:
Data Type:
interface MultipleFileType {
info: [{
fileName: string;
file: File
}]
}
Validation Schema:
const multipleSchema = z.object({
fileName: z.string({ required_error: "Name is required" }).min(5, { message: "Minimum length is 5" }),
file: z
.any()
.refine((files) => files?.length == 1, 'Image is required.')
.refine((files) => files?.[0]?.size <= (1024 * 1024 * 2.5), `Max file size is 2.5 MB.`)
.refine(
(files) => ['image/jpeg', 'image/jpg', 'image/png'].includes(files?.[0]?.type),
'.jpg, .jpeg, .png and .webp files are accepted.',
),
})
export const multipleFileUploaderSchema = z.object({
info: z.array(
multipleSchema
)
})
Form Structure:
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
{fields.map((item, index) => (
<div key={item.id}>
<div className="md:flex md:flex-col">
<FormField
key={index}
control={form.control}
name={`info.${index}.fileName`} <------- ERROR 1
render={({ field }) => (
<FormItem>
<FormLabel>Filename</FormLabel>
<FormControl>
<Input type="text" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="md:flex md:flex-col">
<FormField
key={index}
control={form.control}
name={`info.${index}.file`} <------- ERROR 2
render={({ field: { onChange, ...fieldProps } }) => (
<FormItem>
<FormLabel>Picture</FormLabel>
<FormControl>
<input
type="file"
{...fieldProps}
value=""
onChange={(event) => {
const { files } = getImageData(event, index);
// setPreviewUrl(displayUrl);
onChange(files);
}}
ref={imgInputRef}
hidden
/>
</FormControl>
{/* <FormMessage /> */}
</FormItem>
)}
/>
</div>
<Button
disabled={fields.length === 1}
className="text-red-500"
variant="outline"
size="icon"
type="button"
onClick={() => remove(index)}
>
<Trash className="h-4 w-4" />
</Button>
<Button
disabled={fields.length > 2.5 * 1024 * 1024 || !form.formState.isValid}
className="ml-4"
variant="outline"
size="icon"
type="button"
onClick={() =>
append({
fileName: "",
file: new File([], "/vite.svg"),
})
}
>
<Plus className="h-4 w-4" />
</Button>
</div>
))}
<Button type="submit" className="ml-4">
Submit
</Button>
</form>
</Form>;
The issue marked as ERROR 1 and ERROR 2 in the code snippet is as follows:
Type '
info.${number}.fileName
' is not assignable to type '"info" | "info.0" | "info.0.fileName" | "info.0.file"'.ts(2322) controller.d.ts(20, 5): The expected type comes from property 'name' which is declared here on type 'IntrinsicAttributes & { render: ({ field, fieldState, formState, }: { field: ControllerRenderProps<MultipleFileType, "info" | "info.0" | "info.0.fileName" | "info.0.file">; fieldState: ControllerFieldState; formState: UseFormStateReturn<...>; }) => ReactElement<...>; } & UseControllerProps<...>'
Any suggestions or feedback would be greatly appreciated.