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)