Form schema
Loop
Learn how the Loop element is used in the schema.
Usage
The Loop element is used to define a loop.
To understand how it is used let's look at this example:
import type { Schema, Form, Loop, Return, Variables } from "@formity/react";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { FormView, FormLayout, Select, Next, Back } from "./components";
import { Controller } from "./controller";
export type Values = [
Variables<{ languages: { value: string; question: string }[] }>,
Variables<{
i: number;
languagesRatings: { name: string; rating: string }[];
}>,
Loop<
[
Variables<{ language: { value: string; question: string } }>,
Form<{ rating: string }>,
Variables<{
i: number;
languagesRatings: { name: string; rating: string }[];
}>,
]
>,
Return<{ languagesRatings: { name: string; rating: string }[] }>,
];
export const schema: Schema<Values> = [
{
variables: () => ({
languages: [
{
value: "javascript",
question: "What rating would you give to the JavaScript language?",
},
{
value: "python",
question: "What rating would you give to the Python language?",
},
{
value: "go",
question: "What rating would you give to the Go language?",
},
],
}),
},
{
variables: () => ({
i: 0,
languagesRatings: [],
}),
},
{
loop: {
while: ({ i, languages }) => i < languages.length,
do: [
{
variables: ({ i, languages }) => ({
language: languages[i],
}),
},
{
form: {
values: ({ language }) => ({
rating: ["love-it", [language.value]],
}),
render: ({
inputs,
values,
onNext,
onBack,
getState,
setState,
}) => (
<Controller
step={`rating-${inputs.language.value}`}
onNext={onNext}
onBack={onBack}
getState={getState}
setState={setState}
>
<FormView
defaultValues={values}
resolver={zodResolver(
z.object({
rating: z.string(),
}),
)}
>
<FormLayout
heading={inputs.language.question}
description="We would like to know how much you like it"
fields={[
<Select
key="rating"
name="rating"
label="Rating"
options={[
{ value: "love-it", label: "Love it" },
{ value: "like-it-a-lot", label: "Like it a lot" },
{ value: "it-is-okay", label: "It is okay" },
]}
direction="y"
/>,
]}
button={<Next>Next</Next>}
back={inputs.i > 0 ? <Back /> : undefined}
/>
</FormView>
</Controller>
),
},
},
{
variables: ({ i, languagesRatings, language, rating }) => ({
i: i + 1,
languagesRatings: [
...languagesRatings,
{ name: language.value, rating },
],
}),
},
],
},
},
{
return: ({ languagesRatings }) => ({
languagesRatings,
}),
},
];
We need to use the Loop
type with the corresponding types:
export type Values = [
// ...
Loop<
[
Variables<{ language: { value: string; question: string } }>,
Form<{ rating: string }>,
Variables<{
i: number;
languagesRatings: { name: string; rating: string }[];
}>,
]
>,
// ...
];
Then, in the schema we need to create an object with the following structure:
export const schema: Schema<Values> = [
// ...
{
loop: {
while: ({ i, languages }) => i < languages.length,
do: [
// ...
],
},
},
// ...
];
The while
property is a function that takes the values generated in previous steps and returns a boolean value. When the value is true, the elements in the do
property are used.
It is important to note that the step
prop is dynamically generated to ensure a unique value for each iteration. Additionally, a value is passed in the rating array to prevent the value from persisting across iterations:
export const schema: Schema<Values> = [
// ...
{
loop: {
while: ({ i, languages }) => i < languages.length,
do: [
// ...
{
form: {
values: ({ language }) => ({
rating: ["love-it", [language.value]],
}),
render: ({
inputs,
values,
onNext,
onBack,
getState,
setState,
}) => (
<Controller
step={`rating-${inputs.language.value}`}
onNext={onNext}
onBack={onBack}
getState={getState}
setState={setState}
>
<FormView
defaultValues={values}
resolver={zodResolver(
z.object({
rating: z.string(),
}),
)}
>
<FormLayout
heading={inputs.language.question}
description="We would like to know how much you like it"
fields={[
<Select
key="rating"
name="rating"
label="Rating"
options={[
{ value: "love-it", label: "Love it" },
{ value: "like-it-a-lot", label: "Like it a lot" },
{ value: "it-is-okay", label: "It is okay" },
]}
direction="y"
/>,
]}
button={<Next>Next</Next>}
back={inputs.i > 0 ? <Back /> : undefined}
/>
</FormView>
</Controller>
),
},
},
// ...
],
},
},
// ...
];