Form Submission and Validation - anastasiamexa/react-complete-guide-course-resources GitHub Wiki

Submit using state and FormData

Using useState and FormData in React forms is considered good practice because they provide a clean and efficient way to manage state and handle form data. Let's explore the reasons why these are considered beneficial:

1. State Management with useState:

  • Declarative State Updates: useState allows you to declare state variables and provides a way to update them. This promotes a declarative approach to managing state, making it easier to reason about the application's behavior.
  • Functional Updates: useState provides a functional way to update state based on the previous state. This is particularly useful when dealing with asynchronous state updates or when the new state depends on the previous state.
  • Component Re-rendering: State changes trigger a re-render of the component, ensuring that the UI reflects the current state of the application. This is essential for building dynamic and responsive user interfaces.

2. Handling Form Data with FormData:

  • Easy Form Data Collection: FormData simplifies the process of collecting form data. It automatically captures form fields and their values, including file inputs and multiple selections.
  • Dynamic Form Structure: With FormData, you don't need to manually update the form data structure when adding or removing form fields. It adapts dynamically to changes in the form, reducing maintenance efforts.
  • Binary Data Support: FormData supports the inclusion of binary data, making it suitable for forms that involve file uploads. This is essential for handling file input fields.

3. Separation of Concerns:

  • Clear Separation of UI and State Logic: Using state management with useState promotes a clear separation of concerns. The UI components focus on rendering the user interface, while state logic resides in the component's functional code.
  • Decoupling UI Logic from Data Handling: The use of FormData decouples the logic for handling form data from the intricacies of the DOM. This separation enhances code readability and maintainability.

4. Scalability and Maintainability:

  • Readability and Maintainability: The combination of useState and FormData leads to cleaner and more readable code. This is particularly beneficial as the complexity of the form and its data handling logic increases.
  • Adaptability to Changes: The use of state and FormData facilitates easy adaptation to changes in form requirements or structure. As new form fields are added or existing ones are modified, the code can remain concise and adaptable.

In summary, using useState for state management and FormData for handling form data aligns well with React's principles of declarative programming, simplicity, and separation of concerns. These practices contribute to more maintainable, scalable, and readable code in React applications.

Example

import { useState } from "react";

export default function Signup() {
  const [passwordsAreNotEqual, setPasswordsAreNotEqual] = useState(false);

  // Handle the form submission using FormData
  function handleSubmit(event) {
    event.preventDefault();
    // name must have been set on the input elements
    const formData = new FormData(event.target);
    // Multiple values can be selected for the same name
    const acquisitionChannel = formData.getAll('acquisition');
    const data = Object.fromEntries(formData.entries());
    // Add the acquisition channel to the data object
    data.acquisition = acquisitionChannel;

    if (data.password !== data['confirm-password']) {
      setPasswordsAreNotEqual(true);
      return;
    }

    console.log(data);
    // Reset the form
    //event.target.reset();
  }

  return (
    <form onSubmit={handleSubmit}>
      <h2>Welcome on board!</h2>
      <p>We just need a little bit of data from you to get you started 🚀</p>

      <div className="control">
        <label htmlFor="email">Email</label>
        <input id="email" type="email" name="email" required />
      </div>

      <div className="control-row">
        <div className="control">
          <label htmlFor="password">Password</label>
          <input id="password" type="password" name="password" required minLength={6} />
        </div>

        <div className="control">
          <label htmlFor="confirm-password">Confirm Password</label>
          <input
            id="confirm-password"
            type="password"
            name="confirm-password"
            required
          />
          <div className="control-error">
            {passwordsAreNotEqual && 'The passwords do not match'}
          </div>
        </div>
      </div>
    </form>
  );
}

useInput() Custom Hook

The following example represents a login form component in React, and it utilizes a reusable custom hook named useInput for managing the state and validation of input fields. Let's break down the code and explain it step by step:

// useInput.js
import { useState } from 'react';

export function useInput(defaultValue, validationFn) {
  const [enteredValue, setEnteredValue] = useState(defaultValue);
  const [didEdit, setDidEdit] = useState(false);
  const valueIsValid = validationFn(enteredValue);

  function handleInputChange(event) {
    setEnteredValue(event.target.value);
    setDidEdit(false);
  }

  function handleInputBlur() {
    setDidEdit(true);
  }

  return {
    value: enteredValue,
    handleInputChange,
    handleInputBlur,
    hasError: didEdit && !valueIsValid
  };
}

Explanation:

  • useInput is a custom hook that takes an initial defaultValue and a validationFn function as parameters.
  • enteredValue state variable holds the current value of the input field.
  • didEdit state variable tracks whether the input has been edited (blurred) or not.
  • valueIsValid is a boolean indicating whether the current value is valid according to the provided validation function.
  • handleInputChange is a function that updates the enteredValue when the input value changes. It also resets the didEdit flag to false.
  • handleInputBlur is a function that sets the didEdit flag to true when the input field is blurred.
  • The hook returns an object with properties:
    • value: the current value of the input.
    • handleInputChange: a function to handle input changes.
    • handleInputBlur: a function to handle input blur events.
    • hasError: a boolean indicating whether the input has an error based on validation.

Example of usage in a Login component:

// Login.jsx
import Input from './Input.jsx';
import { isEmail, isNotEmpty, hasMinLength } from '../util/validation.js';
import { useInput } from '../hooks/useInput.js';

export default function Login() {
  const {
    value: emailValue,
    handleInputChange: handleEmailChange,
    handleInputBlur: handleEmailBlur,
    hasError: emailHasError,
  } = useInput('', (value) => isEmail(value) && isNotEmpty(value));

  const {
    value: passwordValue,
    handleInputChange: handlePasswordChange,
    handleInputBlur: handlePasswordBlur,
    hasError: passwordHasError,
  } = useInput('', (value) => hasMinLength(value, 6));

  function handleSubmit(event) {
    event.preventDefault();

    if (emailHasError || passwordHasError) {
      return;
    }

    console.log(emailValue, passwordValue);
  }

  return (
    <form onSubmit={handleSubmit}>
      <h2>Login</h2>

      <div className="control-row">
        <Input
          label="Email"
          id="email"
          type="email"
          name="email"
          onBlur={handleEmailBlur}
          onChange={handleEmailChange}
          value={emailValue}
          error={emailHasError && 'Please enter a valid email!'}
        />

        <Input
          label="Password"
          id="password"
          type="password"
          name="password"
          onChange={handlePasswordChange}
          onBlur={handlePasswordBlur}
          value={passwordValue}
          error={passwordHasError && 'Please enter a valid password!'}
        />
      </div>

      <p className="form-actions">
        <button className="button button-flat">Reset</button>
        <button className="button">Login</button>
      </p>
    </form>
  );
}

Explanation:

  • The Login component imports the useInput hook and validation functions from external sources.
  • Two instances of the useInput hook are used to manage the state and validation of the email and password input fields.
  • handleSubmit function is triggered on form submission. It checks if there are any validation errors before logging the email and password values to the console.
  • The component renders a form with email and password input fields, utilizing the Input component (which is assumed to handle rendering input fields consistently).
  • Validation errors are displayed beneath the respective input fields.

In summary, this code illustrates a modular and reusable approach to handling input state and validation in a login form using a custom hook (useInput). The separation of concerns between the Login component and the useInput hook makes the code more maintainable and easy to understand.

⚠️ **GitHub.com Fallback** ⚠️