I have a JS objects like below (this is just one of many)
const formInput= {
Service_Name: {
required: true,
type: "text",
constraint: {
min: 3,
max: 15,
format: "alpha-hyphen-numeric"
}
},
Content_Type: {
required: true,
type: "select",
options: ["Markdown", "HTML"]
},
Remarks: {
required: false,
type: "text",
constraint: {
max: 30
}
}
};
I have to create form to capture the inputs of these fields. Since there are multiple JS objects (in reality they are json
‘s) like this the form creation has to be dynamic. I have accomplished this like so
import React from "react";
import { useForm } from "react-hook-form";
import { TextInput } from "./textInput";
import { SelectInput } from "./selectInput";
import { FiledType } from "./formTypes";
const formInput: FiledType = {
Service_Name: {
required: true,
type: "text",
constraint: {
min: 3,
max: 15,
format: "alpha-hyphen-numeric"
}
},
Content_Type: {
required: true,
type: "select",
options: ["Markdown", "HTML"]
},
Remarks: {
required: false,
type: "text",
constraint: {
max: 30
}
}
};
export default App = (): JSX.Element => {
const form = useForm({ mode: "all" });
const { register, handleSubmit, formState } = form;
const { errors } = formState;
const onSubmit = (data: any) => {
console.log(data);
};
return (
<React.Fragment>
<div>
<form onSubmit={handleSubmit(onSubmit)} noValidate>
{Object.keys(formInput).map((item) =>
formInput[item].type === "text" ? (
<TextInput
key={item}
name={item}
formItemData={formInput[item]}
register={register}
errors={errors}
/>
) : (
<SelectInput
key={item}
name={item}
formItemData={formInput[item]}
register={register}
errors={errors}
/>
)
)}
<button>Submit</button>
</form>
</div>
</React.Fragment>
);
};
Based on "type" in the json
, one of the TextInput
, DateInput
or SelectInput
is rendered. The TextInput
component is like below.
import React from "react";
import { FormFieldPropType } from "./formTypes";
export const TextInput = ({
register,
name,
formItemData,
errors
}: FormFieldPropType) => {
const formName = name.replace(/s+/g, "-").toLowerCase();
return (
<div>
<label htmlFor={formName}>{name}</label>
<div>
<input
{...register(formName, {
required: formItemData["required"],
maxLength: formItemData["constraint"]?.max,
minLength: formItemData["constraint"]?.min
})}
type="text"
id={formName}
name={formName}
placeholder={name}
/>
</div>
</div>
);
};
All of this is working fine. However, now there is a requirement to render additional field(s) based on the content of another filed. For example, if the user selects Markdown
for the field "Content Type", I should add a "text" filed right under it named "Folder Location". If the user selects HTML
, nothing needs to be done.
I am not able to figure out how to add this additional filed conditionally. Can any one help? Thanks.
The working code (without syyling) can be found here -> https://codesandbox.io/s/reac-hook-form-r66n5w?file=/src/textInput.tsx
2
Answers
To achieve this, you will need to keep track of the values of your form fields. Fortunately, react-hook-form provides the watch function, which allows you to monitor the value of a specified input. Using watch, you can conditionally render the additional "Folder Location" field when the "Content Type" is "Markdown".
Here’s how you can implement this:
Use watch to monitor the Content_Type field
First, import the watch function from your form object, and use it to watch the Content_Type field:
Conditionally render the "Folder Location" input
You can now check the contentTypeValue to decide whether to render the "Folder Location" input:
This code will render the "Folder Location" input right below the "Content Type" input if the "Markdown" option is selected.
Update the App component
Incorporate the above changes into your App component:
By implementing the above changes, you’ll be able to render the "Folder Location" field conditionally based on the value of the "Content Type" field. Make sure to adjust the rest of your components accordingly to handle the newly added "Folder Location" field (e.g., validating its input if necessary). Let me know if this helps you mate, if you have anymore questions feel free to ask
If Derrick’s proposed soultion didn’t work for you for some reason, Try this alternative.(tho Derrick’s solution seems more elegant)
In the App component include a state for the selected content type and an eventHandler to update the state. Then passdown the handler to Select component
In the SelectInput component, pass the onChange prop to the select element to handle the selection change
Add the to be conditionally rendered component to your form.
Also you will need to update FormFieldPropType
edit: here’s a working sandbox link