DEV Community

Cover image for API Driven React-Hook Form (Dynamic Forms)
Muhammad Azfar Aslam
Muhammad Azfar Aslam

Posted on

API Driven React-Hook Form (Dynamic Forms)

Salut,

Table of content

  1. Introduction
  2. Dynamic Form Data
  3. React Hook Form
  4. Text Input Field
  5. Text Area Field
  6. Radio Field
  7. Dropdown Field
  8. Checkbox Field
  9. Error Component
  10. Complete Input Component

Introduction

API-driven forms refer to the practice of using APIs (Application Programming Interfaces) to integrate and manage forms within web applications. This approach offers several benefits that can enhance the functionality and user experience of forms on websites or applications.

  • Dynamic Data Loading
  • Real-time Validation
  • Flexibility and Customization
  • Secure Data Handling
  • Seamless Integration with Backends
  • Consistency Across Platforms
  • Reduced Development Time
  • Scalability
  • Easier Updates and Maintenance

Prerequisite - React Hook Form. As this article is related to API-driven forms, that's why I'll now elaborate on the working of React Hook Form. However, if you need an understanding of this library, just leave a comment and I'll explain it in another article.

Dynamic Form Data

When we call an API for form fields, it should have the following payload. I am going to write my code accordingly to this payload if you guys want a different payload. You will be required to make changes in the code accordingly. I am not calling any API in this article, just creating a variable "dynamicForm" and assigning payload to it.

 const dynamicForm = { firstName: { label: "First Name", type: "text", placeholder: "", defaultValue: "Yes", rules: { required: true, }, }, lastName: { label: "Last Name", type: "text", placeholder: "", defaultValue: "", rules: { required: true, }, }, email: { label: "Email", type: "email", placeholder: "", defaultValue: "", rules: { required: true, }, }, subject: { label: "Subject", type: "dropdown", options: ["Chemistry", "Physics", "Arts"], defaultValue: "", rules: { required: true, }, }, message: { label: "Message", type: "textarea", placeholder: "", defaultValue: "", rules: { required: false, }, }, } 
Enter fullscreen mode Exit fullscreen mode

React Hook Form

I will make a component named "Input" but for now let's suppose there is a component. Now, let's call the form and this Input component.

 const { handleSubmit, control, formState: { errors }, } = useForm(); 
Enter fullscreen mode Exit fullscreen mode

Let's convert dynamicForm object to array and map through it the Input component.

 const formInputs = Object.keys(dynamicForm).map((e, i) => { const { rules, defaultValue, label } = dynamicForm[e]; return ( <section key={i}> <label className="grey"> {label} {rules?.required ? <span className="red">*</span> : ""} </label> <Controller name={e} control={control} rules={rules} defaultValue={defaultValue} render={({ field }) => ( <div> <Input cClass="input-1" value={field.value} onChange={field.onChange} {...dynamicForm[e]} /> </div> )} /> {errors[e] && <Error>This field is required</Error>} </section> ); }); 
Enter fullscreen mode Exit fullscreen mode

Now, add a submit handler and form to our main component.

 const onSubmit = (data) => console.log(data); return ( <div className="wrapper"> <form onSubmit={handleSubmit(onSubmit)}> {formInputs} <div> <button type="submit" className="btn-1"> Submit </button> </div> </form> </div> ); 
Enter fullscreen mode Exit fullscreen mode

Text Input Field

 const Input = ({ cClass, value, onChange, type, ...rest }) => { return ( <input type='text' placeholder={rest?.placeholder} onChange={(e) => onChange(e.target.value)} value={value} className={cClass} /> ); } 
Enter fullscreen mode Exit fullscreen mode

Text Area Field

 const Input = ({ cClass, value, onChange, type, ...rest }) => { return ( <textarea name="" id="" cols="30" rows="10" className={cClass} onChange={(e) => onChange(e.target.value)}>{value}</textarea> ); } 
Enter fullscreen mode Exit fullscreen mode

Radio Field

 const Input = ({ cClass, value, onChange, type, ...rest }) => { return rest?.options.map((i) => ( <label> <input type='radio' key={i} value={i} onChange={(e) => onChange(e.target.value)} checked={value === i} /> {i} </label> )); } 
Enter fullscreen mode Exit fullscreen mode

Dropdown Field

 const Input = ({ cClass, value, onChange, type, ...rest }) => { return ( <select value={value} onChange={(e) => onChange(e.target.value)} className={cClass}> <option value="">Select an option</option> {rest?.options.map((option) => ( <option key={option} value={option}> {option} </option> ))} </select> ); } 
Enter fullscreen mode Exit fullscreen mode

CheckBox Field

 const Input = ({ cClass, value, onChange, type, ...rest }) => { return ( <label> <input type="checkbox" onChange={(e) => onChange(e.target.checked)} checked={value} /> {rest?.checkboxLabel} </label> ); } 
Enter fullscreen mode Exit fullscreen mode

I used a switch statement to check the type.

Error Component

 export const Error = ({ children }) => <p style={{ color: "red" }}>{children}</p>; 
Enter fullscreen mode Exit fullscreen mode

Complete Input Component

 const Input = ({ cClass, value, onChange, type, ...rest }) => { switch (type) { case "text": return ( <input type='text' placeholder={rest?.placeholder} onChange={(e) => onChange(e.target.value)} value={value} className={cClass} /> ); case "email": return ( <input type='email' placeholder={rest?.placeholder} onChange={(e) => onChange(e.target.value)} value={value} className={cClass} /> ); case "textarea": return ( <textarea name="" id="" cols="30" rows="10" className={cClass} onChange={(e) => onChange(e.target.value)}>{value}</textarea> ); case "radio": return rest?.options.map((i) => ( <label> <input type='radio' key={i} value={i} onChange={(e) => onChange(e.target.value)} checked={value === i} /> {i} </label> )); case "dropdown": return ( <select value={value} onChange={(e) => onChange(e.target.value)} className={cClass}> <option value="">Select an option</option> {rest?.options.map((option) => ( <option key={option} value={option}> {option} </option> ))} </select> ); case "checkbox": return ( <label> <input type="checkbox" onChange={(e) => onChange(e.target.checked)} checked={value} /> {rest?.checkboxLabel} </label> ); default: return null; } }; export default Input 
Enter fullscreen mode Exit fullscreen mode

Frontend would render like this.

Image description

Thank you for reading this far. This is a brief introduction of API Driven forms
If you find this article useful, please like ❤ and share this article. Someone could find it useful too. If you find anything technically inaccurate please feel free to leave a comment.
Hope it's a nice and informative read for you. Follow me on LinkedIn for more helpful resources or reach out for any mutual opportunity.

Few more articles:

Top comments (0)