Skip to content

React Forms

Forms are essential in web applications for collecting user input, handling interactions, and submitting data. In React, form management can be handled through controlled components, uncontrolled components, and form validation techniques, each with unique benefits. This guide covers these concepts with examples, helping you choose the best approach for your project.

Prerequisites

Before diving in, you should have:

  • Basic understanding of React and its hooks
  • Familiarity with HTML forms
  • Node.js and npm installed

For form validation libraries, you’ll need to install:

Terminal window
# For Formik
npm install formik
# For React Hook Form
npm install react-hook-form
# For Yup validation
npm install yup

Controlled Components

Controlled components in React use state to control form elements, updating in real time as users type. This approach allows for immediate data management and validation directly within the component’s state.

Steps

  1. Define State for Form Inputs
    • Each form element (e.g., input, select or textarea) requires a state to store its value.
  2. Update State on User Input
    • Use event handlers to update state and re-render the component with each change.

Examples: Controlled Form Component

Install the following libraries. Assuming you have installed

Terminal window
npm install react react-dom

Benefits of Controlled Components

  1. Immediate access to form data
  2. Ability to implement instant validation
  3. Conditional rendering based on form state
  4. Easier implementation of form logic

Uncontrolled Components

Uncontrolled components in React are form elements that manage their own state internally rather than relying on React’s state. This means that the input values are managed via refs (references) instead of the component’s state. Uncontrolled components are useful when you want to avoid unnecessary re-renders or when you need to integrate with non-React code that relies on the DOM.

Key Concepts

  • Refs: Use React.createRef() or the useRef hook to create a reference to a DOM element.
  • Direct Access: Access the input’s value directly from the DOM instead of keeping it in the component’s state.

Steps

  1. Create a Ref: Initialize a ref using useRef or createRef.
  2. Attach the Ref to the Input: Use the ref attribute on the form input elements.
  3. Access the Value: Use the ref to access the input value when needed, usually upon form submission.

Example: Uncontrolled Form Component

UncontrolledForm.jsx
import React, { useRef } from 'react';
function UncontrolledForm() {
const nameRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
alert(`A name was submitted: ${nameRef.current.value}`);
// You can access the value directly from the ref
};
return (
<form onSubmit={handleSubmit}>
<label>Name:</label>
<input type="text" ref={nameRef} />
<button type="submit">Submit</button>
</form>
);
}

In this example, the nameRef is created using the useRef hook and attached to the input element. Upon form submission, the value can be accessed directly from nameRef.current.value, allowing you to handle the form input without using state.

Benefits of Uncontrolled Components

  1. Less Boilerplate: Less code is needed for handling state, especially for simple forms.
  2. Direct DOM Access: Useful for integrating with third-party libraries or handling non-React components.

When to Use Uncontrolled Components

Uncontrolled components are suitable when:

  1. You have a simple form and don’t need to react to every input change.
  2. You need to interact with legacy code or libraries that require direct DOM manipulation.
  3. You want to optimize performance in scenarios with many form elements.

Form validation

Form validation is a crucial aspect of web applications that ensures the integrity and correctness of user inputs. In React, there are several approaches to validate forms, including manual validation techniques and the use of popular libraries. This guide will delve into these approaches, their advantages, and their disadvantages.

Manual Validation Techniques

Manual validation involves checking input values directly within your React component, typically using state to store form data and validate it before submission.

Example: Manual Validation

Here’s an example of a simple form with manual validation:

MaualValidation.jsx
import React, { useState } from 'react';
function ManualValidationForm() {
const [name, setName] = useState('');
const [error, setError] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (name.trim() === '') {
setError('Name is required');
} else {
setError('');
alert(`Form submitted: ${name}`);
}
};
return (
<form onSubmit={handleSubmit}>
<label>Name:</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
<button type="submit">Submit</button>
</form>
);
}

Pros and Cons of Manual Validation

Pros:

Simplicity: Easy to implement for simple forms with minimal validation requirements. Full Control: You have complete control over the validation logic and error handling.

Cons:

Scalability: As forms become more complex, managing validation can lead to cumbersome code. Reusability: Difficult to reuse validation logic across multiple forms.

Using Libraries for Form Validation

Libraries like Formik, React Hook Form, and Yup provide robust solutions for managing form state and validation.

Examples

Install the following libraries. Assuming you have installed

Terminal window
# For Formik
npm install formik
# For React Hook Form
npm install react-hook-form
# For Yup validation
npm install yup

Pros and Cons of Using Libraries

Pros:

  1. Simplified Logic: Libraries handle much of the boilerplate code, allowing you to focus on your application’s logic.
  2. Built-in Validation: Libraries like Formik and Yup provide schema-based validation, making it easy to manage complex rules.
  3. Performance Optimization: Libraries like React Hook Form optimize rendering and state management, reducing unnecessary re-renders.
  4. Reusability: Create reusable form components and validation schemas, promoting DRY (Don’t Repeat Yourself) principles.

Cons:

  1. Learning Curve: New libraries require time to learn, especially if you’re unfamiliar with their APIs.
  2. Dependency Overhead: Adding additional libraries can increase bundle size and project complexity.
  3. Less Control: While libraries offer convenience, they may abstract away some control over how forms behave and validate.

Comparison of Approaches

FeatureManual ValidationFormik/React Hook Form
Ease of UseSimple for small formsMore complex setup but easier for larger forms
ScalabilityDifficult to scaleHighly scalable
ReusabilityLowHigh (can create reusable components)
ValidationManual checksIntegrated validation with schema support
PerformanceCan be inefficient with many inputsOptimized for performance, only updates on changes

Handling Form Submissions in React

Handling form submissions is a critical aspect of web applications that allows you to collect user input and send it to a back-end service or API. This guide covers best practices for both controlled and uncontrolled components in React, ensuring you can efficiently manage form submissions.

Overview

When working with forms in React, it’s essential to understand how to:

  • Prevent the default form submission behavior.
  • Collect form data effectively.
  • Send the collected data to a back-end service or API.

Preventing Default Form Submission

In React, when a form is submitted, the default behavior is to reload the page. To prevent this, you can use the preventDefault() method within the form’s submit event handler.

Example: Preventing Default Form Submission

Terminal window
import React, { useState, useRef } from 'react';
function ControlledForm() {
const [name, setName] = useState("");
const handleSubmit = (e) => {
e.preventDefault(); // Prevent the default form submission
console.log("Submitted Name:", name);
// You can add your form submission logic here (e.g., API call)
};
return (
<form onSubmit={handleSubmit}>
<label htmlFor="name">Name:</label>
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
);
}
function UncontrolledForm() {
const nameRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault(); // Prevent the default form submission
console.log("Submitted Name:", nameRef.current.value);
// You can add your form submission logic here (e.g., API call)
};
return (
<form onSubmit={handleSubmit}>
<label htmlFor="name">Name:</label>
<input type="text" id="name" ref={nameRef} />
<button type="submit">Submit</button>
</form>
);
}
export default function FormExample() {
return (
<div>
<h2>Controlled Form</h2>
<ControlledForm />
<h2>Uncontrolled Form</h2>
<UncontrolledForm />
</div>
);
}

Collecting Form Data in Controlled Components

Controlled components use React’s state to manage form inputs. Here’s how to handle form submissions in controlled components:

Example: Controlled Form Submission

Terminal window
import React, { useState } from 'react';
function ControlledForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (e) => {
e.preventDefault(); // Prevent default form submission
const formData = { name, email };
// Send formData to a back-end API
console.log('Form submitted:', formData);
// Example of sending data with fetch
// fetch('https://api.example.com/submit', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify(formData),
// })
// .then((response) => response.json())
// .then((data) => console.log('Success:', data))
// .catch((error) => console.error('Error:', error));
};
return (
<form onSubmit={handleSubmit}>
<label>Name:</label>
<input type="text" value={name} onChange={(e) => setName(e.target.value)} />
<br />
<label>Email:</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
<br />
<button type="submit">Submit</button>
</form>
);
}

Collecting Form Data in Uncontrolled Components

Uncontrolled components use refs to access form inputs directly. Here’s how to handle form submissions in uncontrolled components:

Example: Uncontrolled Form submission

Terminal window
import React, { useRef } from 'react';
function UncontrolledForm() {
const nameRef = useRef(null);
const emailRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault(); // Prevent default form submission
const formData = {
name: nameRef.current.value,
email: emailRef.current.value,
};
// Send formData to a back-end API
console.log('Form submitted:', formData);
// Example of sending data with fetch
// fetch('https://api.example.com/submit', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify(formData),
// })
// .then((response) => response.json())
// .then((data) => console.log('Success:', data))
// .catch((error) => console.error('Error:', error));
};
return (
<form onSubmit={handleSubmit}>
<label>Name:</label>
<input type="text" ref={nameRef} />
<br />
<label>Email:</label>
<input type="email" ref={emailRef} />
<br />
<button type="submit">Submit</button>
</form>
);
}

Best Practices for Handling Form Submissions

  1. Prevent Default Behavior: Always prevent the default form submission behavior using e.preventDefault() to avoid page reloads.
  2. Validate Input Data: Before sending data to your API, validate the input fields to ensure they meet your requirements (e.g., required fields, valid email format).
  3. Use a State Management Solution: For larger applications, consider using a state management solution (like Redux or Context API) to manage form state.
  4. Handle Errors Gracefully: Implement error handling for your API requests to provide feedback to users in case of failures.
  5. Provide User Feedback: Show loading indicators or success/error messages after form submission to enhance user experience.

Sending Data to a Back-end Service

Once you have collected the form data, you can send it to a back-end service using the fetch API or a library like Axios. Here’s an example of how to send a POST request:

Terminal window
fetch('https://api.example.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
})
.then((response) => response.json())
.then((data) => console.log('Success:', data))
.catch((error) => console.error('Error:', error));