My task is to do create a form that will get some fields from STRAPI api.
A form will be component, that will be passed to different pages. So in each page there will be same form but with different fields based on Strapi.
Form structure will look like this:
const apiCall = {
dropdown: [
// there can be 0 objects or infinite objects, based on strapi api call
{
question: "how are you?",
answers: [
{name: "Good"},
{name: "Bad"}
// infinite number of answers
]
},
{
question: "how are you?",
answers: [
{name: "Good"},
{name: "Bad"}
// infinite number of answers
]
}
]
}
Then in Formik i need to do something like this:
// DROPDOWNS
{apiCall.dropdown?.map(({ answers, question, id}) => (
<div key={id} className="mb-5">
<h2 className="h3 mb-2">
{question}
</h2>
<FormSelect
key={id}
name={} //??? IDK
label={t('validation.oneOfOptions')}
options={
answers?.map(a => (
{value: a.name, label: a.name}
))
}
className={formFieldClasses}
/>
</div>
How should i initialize formik values and validate data? I am using Formik component. Then i need to send data to another api call where we are going to send emails with answers. But IDK how can i send dynamic data when i dont know data that will come from apiCall?
Normally i will do something like this:
const initialValues: LostFormValues = {
category: CategoryValues.WALLET,
secCategory: SecCategoryValues.TRAIN,
priority: PriorityValues.HIGH,
cityFrom: null,
cityTo: null,
date: today,
ticketNumber: user?.user?.accountCode || '',
firstName: user?.user?.firstName || '',
surname: user?.user?.surname || '',
email: user?.user?.email || '',
phoneNumber: user?.user?.phoneNumber || '',
description: '',
files: [],
agreeWithTerms: false,
};
and then:
<Formik
enableReinitialize
initialValues={initialValues}
validationSchema={yup.object().shape({
category: yupFieldEnum(CategoryValues),
secCategory: yupFieldEnum(SecCategoryValues),
date: yupField.date,
ticketNumber: yup.string().nullable(), // not required
priority: yupField.string,
cityFrom: yupField.autoComplete,
cityTo: yupField.autoComplete,
firstName: yupField.string,
surname: yupField.string,
email: yupField.email,
description: yupField.string,
agreeWithTerms: yupField.checkbox,
})}
onSubmit={async (values) => {
const isSent = await sendToExternalService(values);
}}
>
But now i cant just write variables and then send them.. i need to write them dynamicaly
so this quite a big question, and a lot of unnecessary info, not sure I'm getting correct all the points however based on api you want to validate answers so if you have field answers
you should have field answer
, basically what i would do:
const apiCall = [{..},{..}]
const ParentComponent = () => {
const [answers, setAnswers] = useState({})
return ( <>
{apiCall.dropdown.map(item => (
<SelectComponent item={item} setAnswers={setAnswers} key={..}/>
)}
}
const SelectComponent = ({item}) => (
const formik = useFormik({
initialValues: {
answer: null,
}
validationSchema: Yup.object().shape({
answer: Yup.string().oneOf(item.answers.map(({name})=> name).nullable()}),
onSubmit: (values) => setAnswers(answers => (
{...answers, [item.question]:values.answer})
);
})
return (
<>...
<FormSelect name='answer'
value={formik.values.answer} options={
item.answers.map(a => (
{value: a.name, label: a.name}
))
}
/>
<Button onClick={() => formik.onSubmit()}>Ok</Button>
</>)
)
So now you keep your answers as key value pairs in parent component and you validate exact question, based on what in the api. Note there is many ways to go from here depending on how you would pass data to api...
Not sure if it would work out of the box if you dynamically change apiCall on fly but this should be also possible to handle