React Hook Form x Zod - Dynamic Schema Object

I'm trying to make an app which helps you practice for Multiple Choice Questions. In the settings page, you add the start number and end number for questions for example: Starts 1, Ends 20. It should then create 20 questions labelled 1, 2, 3...20. I'm using react-hook-form with zod for validation. How can I architect the schema for this? The schema should dynamically have the number of questions and an option between 1-4 as the value. Initially I thought the schema can be an array of an enum (1-4) of a specific length. But since the schema is not an object with keys, I don't have type completion on the name field of the form inputs. My second idea was to make the schema an object with keys q1, q2 ... q20 with every value as the enum (1-4). This would allow me to make the input names q1, q2, etc. However idk how to create this schema object. I have to somehow iterate over an Array(numberOfQuestions) and append entries to the z.object({}) 2 questions: Is ^ this an appropriate solution? If not, what would you suggest. If yes, how would I go about creating a schema z.object({}) with dynamic entries This is what I started with, however, typing this is a nightmare.
let questionsObject
Array(numberOfQuestions).map(
({ _, i }) => (questionsObject[`q${i}`] = z.enum(["1", "2", "3", "4"])),
)
const questionsSchema = z.object(questionsObject)
let questionsObject
Array(numberOfQuestions).map(
({ _, i }) => (questionsObject[`q${i}`] = z.enum(["1", "2", "3", "4"])),
)
const questionsSchema = z.object(questionsObject)
Any suggestions or discussion is welcome
4 Replies
Mocha
Mocha8mo ago
Are you looking for something like this?
z.enum(['1', '2', '3', '4']).array().length(numberOfQuestions)
z.enum(['1', '2', '3', '4']).array().length(numberOfQuestions)
I would just use a normal <form> and parse the FormData into a Map
Neeshツ
Neeshツ8mo ago
Ya I guess I could drop hook forms if I don't really need it
Mocha
Mocha8mo ago
type AnswerType = '1' | '2' | '3' | '4'

function onSubmit(formData: FormData) {
const answers = new Map<string, AnswerType>()
for (const [key, value] of formData.entries()) {
if (isValidAnswer(value)) answers.set(key, value)
}
}

function isValidAnswer(answer: unknown): answer is AnswerType {
return answer === '1' || answer === '2' || answer === '3' || answer === '4'
}
type AnswerType = '1' | '2' | '3' | '4'

function onSubmit(formData: FormData) {
const answers = new Map<string, AnswerType>()
for (const [key, value] of formData.entries()) {
if (isValidAnswer(value)) answers.set(key, value)
}
}

function isValidAnswer(answer: unknown): answer is AnswerType {
return answer === '1' || answer === '2' || answer === '3' || answer === '4'
}
Mocha
Mocha8mo ago
btw I created this package for when Zod is overkill: https://www.npmjs.com/package/formdata-schema
npm
formdata-schema
Parse FormData with a simple schema. Latest version: 0.1.13, last published: 3 months ago. Start using formdata-schema in your project by running npm i formdata-schema. There are no other projects in the npm registry using formdata-schema.
Want results from more Discord servers?
Add your server