Advanced concepts
Conditional fields
Learn how to add fields that appear when a condition is met.
Initial steps
We'll show you how to add fields that appear when a condition is met. To learn how to do it, clone the following GitHub repository so that you don't need to start from scratch.
git clone https://github.com/martiserra99/formity-react-advanced-concepts
Make sure you run the following command to install all the dependencies.
npm install
Conditional field
To create a conditional field, we will create a file named conditional-field.tsx
with the following component.
// conditional-field.tsx
import type { ReactNode } from "react";
import { useFormContext } from "react-hook-form";
interface ConditionalFieldProps<T extends object> {
condition: (values: T) => boolean;
values: string[];
children: ReactNode;
}
export default function ConditionalField<T extends object>({
condition,
values,
children,
}: ConditionalFieldProps<T>) {
const { watch } = useFormContext();
const variables = watch(values).reduce(
(acc, value, index) => ({ ...acc, [values[index]]: value }),
{},
);
if (condition(variables)) {
return children;
}
return null;
}
This component receives a condition function, form value names, and a field. It evaluates the condition using the form values and renders the field if true.
We can now update the schema.tsx
file to use the conditional field.
// schema.tsx
import type { Schema, Form, Return, Variables } from "@formity/react";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Step, Layout, TextField, YesNo, NextButton } from "./components";
import { MultiStep } from "./multi-step";
import ConditionalField from "./conditional-field";
export type Values = [
Form<{ working: boolean; company: string }>,
Variables<{ company: string | null }>,
Return<{ working: boolean; company: string | null }>,
];
export const schema: Schema<Values> = [
{
form: {
values: () => ({
working: [true, []],
company: ["", []],
}),
render: ({ values, onNext, onBack }) => (
<MultiStep onNext={onNext} onBack={onBack}>
<Step
key="working"
defaultValues={values}
resolver={zodResolver(
z
.object({
working: z.boolean(),
company: z.string(),
})
.superRefine((data, ctx) => {
if (data.working) {
if (data.company === "") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Required",
path: ["company"],
});
}
}
}),
)}
>
<Layout
heading="Tell us about yourself"
description="We would want to know about you"
fields={[
<YesNo key="working" name="working" label="Working" />,
<ConditionalField<{ working: boolean }>
key="company"
condition={({ working }) => working}
values={["working"]}
>
<TextField key="company" name="company" label="Company" />
</ConditionalField>,
]}
button={<NextButton>Next</NextButton>}
/>
</Step>
</MultiStep>
),
},
},
{
variables: ({ working, company }) => ({
company: working ? company : null,
}),
},
{
return: ({ working, company }) => ({
working,
company,
}),
},
];
For the conditional field, we need to apply validation rules only when the condition is met. Moreoever, we also define a variable to set a value when the field is hidden.