Form flow
Switch
Learn how the switch element is used in the flow.
Usage
The switch element is used to define multiple conditions.
To understand how it is used let's look at this example.
import { useCallback, useState } from "react";
import {
Formity,
type s,
type Flow,
type OnReturn,
type ReturnOutput,
} from "@formity/react";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Form } from "./components/form";
import { Output } from "./components/output";
type Schema = {
render: React.ReactNode;
struct: [
s.Form<{ interested: string }>,
s.Switch<{
branches: [
[
s.Form<{ whyYes: string }>,
s.Return<{ interested: "yes"; whyYes: string }>,
],
[
s.Form<{ whyNot: string }>,
s.Return<{ interested: "no"; whyNot: string }>,
],
];
default: [
s.Form<{ whyNotSure: string }>,
s.Return<{ interested: "notSure"; whyNotSure: string }>,
];
}>,
];
inputs: Record<never, never>;
params: Record<never, never>;
};
const flow: Flow<Schema> = [
{
form: {
fields: () => ({
interested: ["yes", []],
}),
render: ({ fields, onBack, onNext }) => (
<Form
key="interested"
defaultValues={fields}
resolver={zodResolver(
z.object({
interested: z.string(),
}),
)}
heading="Are you interested in learning how to code?"
content={[
{
type: "select",
name: "interested",
label: "Interested",
placeholder: "Select an option",
options: [
{ value: "yes", label: "Yes, I am interested." },
{ value: "no", label: "No, it is not for me." },
{ value: "maybe", label: "Maybe, I am not sure." },
],
},
]}
buttons={{
back: null,
next: "Next",
}}
onBack={onBack}
onNext={onNext}
/>
),
},
},
{
switch: {
branches: [
{
case: ({ interested }) => interested === "yes",
then: [
{
form: {
fields: () => ({
whyYes: ["", []],
}),
render: ({ fields, onNext, onBack }) => (
<Form
key="whyYes"
defaultValues={fields}
resolver={zodResolver(
z.object({
whyYes: z.string().nonempty("Required"),
}),
)}
heading="Why are you interested?"
content={[
{
type: "textarea",
name: "whyYes",
label: "Why?",
placeholder: "Write your answer here...",
},
]}
buttons={{
back: "Back",
next: "Submit",
}}
onBack={onBack}
onNext={onNext}
/>
),
},
},
{
return: ({ whyYes }) => ({
interested: "yes",
whyYes,
}),
},
],
},
{
case: ({ interested }) => interested === "no",
then: [
{
form: {
fields: () => ({
whyNot: ["", []],
}),
render: ({ fields, onNext, onBack }) => (
<Form
key="whyNot"
defaultValues={fields}
resolver={zodResolver(
z.object({
whyNot: z.string().nonempty("Required"),
}),
)}
heading="Why are you not interested?"
content={[
{
type: "textarea",
name: "whyNot",
label: "Why?",
placeholder: "Write your answer here...",
},
]}
buttons={{
back: "Back",
next: "Submit",
}}
onBack={onBack}
onNext={onNext}
/>
),
},
},
{
return: ({ whyNot }) => ({
interested: "no",
whyNot,
}),
},
],
},
],
default: [
{
form: {
fields: () => ({
whyNotSure: ["", []],
}),
render: ({ fields, onNext, onBack }) => (
<Form
key="whyNotSure"
defaultValues={fields}
resolver={zodResolver(
z.object({
whyNotSure: z.string().nonempty("Required"),
}),
)}
heading="Why are you not sure?"
content={[
{
type: "textarea",
name: "whyNotSure",
label: "Why?",
placeholder: "Write your answer here...",
},
]}
buttons={{
back: "Back",
next: "Submit",
}}
onBack={onBack}
onNext={onNext}
/>
),
},
},
{
return: ({ whyNotSure }) => ({
interested: "notSure",
whyNotSure,
}),
},
],
},
},
];
export default function App() {
const [output, setOutput] = useState<ReturnOutput<Schema> | null>(null);
const onReturn = useCallback<OnReturn<Schema>>((output) => {
setOutput(output);
}, []);
if (output) {
return <Output output={output} onStart={() => setOutput(null)} />;
}
return <Formity<Schema> flow={flow} onReturn={onReturn} />;
}
We need to use the s.Switch type with the corresponding types.
type Schema = {
// ...
struct: [
// ...
s.Switch<{
branches: [
[
s.Form<{ whyYes: string }>,
s.Return<{ interested: "yes"; whyYes: string }>,
],
[
s.Form<{ whyNot: string }>,
s.Return<{ interested: "no"; whyNot: string }>,
],
];
default: [
s.Form<{ whyNotSure: string }>,
s.Return<{ interested: "notSure"; whyNotSure: string }>,
];
}>,
];
// ...
};
Then, in the flow we need to create an object with the following structure.
const flow: Flow<Schema> = [
// ...
{
switch: {
branches: [
{
case: ({ interested }) => interested === "yes",
then: [
{
form: {
fields: () => ({
whyYes: ["", []],
}),
render: ({ fields, onNext, onBack }) => (
<Form
key="whyYes"
defaultValues={fields}
resolver={zodResolver(
z.object({
whyYes: z.string().nonempty("Required"),
}),
)}
heading="Why are you interested?"
content={[
{
type: "textarea",
name: "whyYes",
label: "Why?",
placeholder: "Write your answer here...",
},
]}
buttons={{
back: "Back",
next: "Submit",
}}
onBack={onBack}
onNext={onNext}
/>
),
},
},
{
return: ({ whyYes }) => ({
interested: "yes",
whyYes,
}),
},
],
},
{
case: ({ interested }) => interested === "no",
then: [
{
form: {
fields: () => ({
whyNot: ["", []],
}),
render: ({ fields, onNext, onBack }) => (
<Form
key="whyNot"
defaultValues={fields}
resolver={zodResolver(
z.object({
whyNot: z.string().nonempty("Required"),
}),
)}
heading="Why are you not interested?"
content={[
{
type: "textarea",
name: "whyNot",
label: "Why?",
placeholder: "Write your answer here...",
},
]}
buttons={{
back: "Back",
next: "Submit",
}}
onBack={onBack}
onNext={onNext}
/>
),
},
},
{
return: ({ whyNot }) => ({
interested: "no",
whyNot,
}),
},
],
},
],
default: [
{
form: {
fields: () => ({
whyNotSure: ["", []],
}),
render: ({ fields, onNext, onBack }) => (
<Form
key="whyNotSure"
defaultValues={fields}
resolver={zodResolver(
z.object({
whyNotSure: z.string().nonempty("Required"),
}),
)}
heading="Why are you not sure?"
content={[
{
type: "textarea",
name: "whyNotSure",
label: "Why?",
placeholder: "Write your answer here...",
},
]}
buttons={{
back: "Back",
next: "Submit",
}}
onBack={onBack}
onNext={onNext}
/>
),
},
},
{
return: ({ whyNotSure }) => ({
interested: "notSure",
whyNotSure,
}),
},
],
},
},
];
The branches property is an array of objects with two properties. The case property is a function that returns a boolean value. If it is true, the elements in then are used.
The first condition that evaluates to true is the one that is used. If no case evaluates to true, the elements in default are used.