How to Build Forms in React with real world example

Before we delve into the blog post,let’s take a moment to understand this feature in JavaScript.

In JavaScript, computed property names are denoted by square brackets []. This syntax allows you to dynamically set the keys of an object using variables or expressions. Here’s a quick recap of the syntax:

const propertyName = 'example';
const propertyValue = 42;

const dynamicObject = {
  [propertyName]: propertyValue,
};

console.log(dynamicObject); // Output: { example: 42 }

This example showcases how we can use the variable propertyName to dynamically set the key of the dynamicObject based on its value. This flexibility becomes particularly useful when dealing with dynamic data, such as form inputs in React.

1. Basic Form Setup

import React, { useState } from 'react';

const BasicForm = () => {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prev) => ({ ...prev, [name]: value }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted:', formData);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Username:
        <input type="text" name="username" value={formData.username} onChange={handleChange} />
      </label>
      <label>
        Email:
        <input type="email" name="email" value={formData.email} onChange={handleChange} />
      </label>
      <label>
        Password:
        <input type="password" name="password" value={formData.password} onChange={handleChange} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
};

export default BasicForm;

In this basic example, we use the useState hook to manage form data and update it with the handleChange function. The form is submitted to the console for simplicity.

2. Form Validation

Let’s introduce simple form validation using state to track errors.

import React, { useState } from 'react';

const FormWithValidation = () => {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
  });

  const [formErrors, setFormErrors] = useState({
    username: '',
    email: '',
    password: '',
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prev) => ({ ...prev, [name]: value }));
  };

  const validateForm = () => {
    const errors = {};

    // Basic validation example (you can use a library like Yup for more complex validation)
    if (!formData.username) {
      errors.username = 'Username is required';
    }

    if (!formData.email) {
      errors.email = 'Email is required';
    } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
      errors.email = 'Invalid email address';
    }

    if (!formData.password) {
      errors.password = 'Password is required';
    }

    setFormErrors(errors);
    return Object.keys(errors).length === 0; // Return true if no errors
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    if (validateForm()) {
      console.log('Form submitted:', formData);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Username:
        <input type="text" name="username" value={formData.username} onChange={handleChange} />
        <span style={{ color: 'red' }}>{formErrors.username}</span>
      </label>
      <label>
        Email:
        <input type="email" name="email" value={formData.email} onChange={handleChange} />
        <span style={{ color: 'red' }}>{formErrors.email}</span>
      </label>
      <label>
        Password:
        <input type="password" name="password" value={formData.password} onChange={handleChange} />
        <span style={{ color: 'red' }}>{formErrors.password}</span>
      </label>
      <button type="submit">Submit</button>
    </form>
  );
};

export default FormWithValidation;

This example introduces a validateForm function to check for errors and update the formErrors state accordingly.

3. Dynamic Form Fields

Let’s extend the form to include dynamic fields, allowing users to add or remove input fields.

import React, { useState } from 'react';

const DynamicFormFields = () => {
  const [dynamicFields, setDynamicFields] = useState(['']);

  const handleAddField = () => {
    setDynamicFields([...dynamicFields, '']);
  };

  const handleRemoveField = (index) => {
    const updatedFields = [...dynamicFields];
    updatedFields.splice(index, 1);
    setDynamicFields(updatedFields);
  };

  const handleFieldChange = (index, value) => {
    const updatedFields = [...dynamicFields];
    updatedFields[index] = value;
    setDynamicFields(updatedFields);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Dynamic Fields:', dynamicFields);
  };

  return (
    <form onSubmit={handleSubmit}>
      {dynamicFields.map((field, index) => (
        <div key={index}>
          <input
            type="text"
            value={field}
            onChange={(e) => handleFieldChange(index, e.target.value)}
          />
          <button type="button" onClick={() => handleRemoveField(index)}>
            Remove
          </button>
        </div>
      ))}
      <button type="button" onClick={handleAddField}>
        Add Field
      </button>
      <button type="submit">Submit</button>
    </form>
  );
};

export default DynamicFormFields;

This example introduces a dynamic array of fields and functions to add, remove, and update them.

4. Asynchronous Form Submission

Handle asynchronous form submission, such as sending data to a server and handling responses.

import React, { useState } from 'react';

const AsyncFormSubmission = () => {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
  });

  const [loading, setLoading] = useState(false);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prev) => ({ ...prev, [name]: value }));
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    setLoading(true);

    try {
      // Simulate API call
      const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
        method: 'POST',
        body: JSON.stringify(formData),
        headers: {
          'Content-Type': 'application/json',
        },
      });

      const data = await response.json();
      console.log('Server response:', data);
    } catch (error) {
      console.error('Error submitting form:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Username:
        <input type="text" name="username" value={formData.username} onChange={handleChange} />
      </label>
      <label>
        Email:
        <input type="email" name="email" value={formData.email} onChange={handleChange} />
      </label>
      <label>
        Password:
        <input type="password" name="password" value={formData.password} onChange={handleChange} />
      </label>
      <button type="submit" disabled={loading}>
        {loading ? 'Submitting...' : 'Submit'}
      </button>
    </form>
  );
};

export default AsyncFormSubmission;

This example showcases asynchronous form submission, simulating an API call and handling loading states.

Conclusion

Handling forms in React becomes more powerful as you incorporate state management, validation, dynamic fields, and asynchronous operations. Start with the basics and gradually introduce more advanced concepts based on your application’s requirements. By following these examples, you’ll be better equipped to build and manage complex forms in your React projects. Happy coding! 🚀

Next Post Previous Post
No Comment
Add Comment
comment url