React Hook Form Explained: Simplify Your Form Handling
To use React Hook Form for handling forms and TanStack Query (formerly known as React Query) for data fetching and submission to a backend, you can follow the steps below.
1. Setting Up the Project
If you haven't already, start by creating a React app and installing the necessary dependencies:
npx create-react-app react-hook-form-query
cd react-hook-form-query
npm install react-hook-form @tanstack/react-query axios
2. Setting Up React Query Provider
You'll need to wrap your app with the QueryClientProvider
from TanStack Query so that React Query is available throughout your app.
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
ReactDOM.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>,
document.getElementById('root')
);
3. Building the Registration Form
Create a registration form using React Hook Form and handle the submission with TanStack Query.
// src/App.js
import React from 'react';
import { useForm } from 'react-hook-form';
import axios from 'axios';
import { useMutation } from '@tanstack/react-query';
const App = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
// Define the mutation using React Query's `useMutation`
const mutation = useMutation({
mutationFn: async (formData) => {
const response = await axios.post('https://your-backend-api.com/register', formData);
return response.data;
},
onSuccess: (data) => {
console.log("Registration Successful", data);
},
onError: (error) => {
console.log("Error Occurred", error);
}
});
// Handle form submission
const onSubmit = (data) => {
mutation.mutate(data); // Submit the form data via the mutation
};
return (
<div>
<h1>Register</h1>
<form onSubmit={handleSubmit(onSubmit)}>
{/* Name Field */}
<div>
<label>Name:</label>
<input
{...register("name", { required: "Name is required" })}
placeholder="Enter your name"
/>
{errors.name && <p>{errors.name.message}</p>}
</div>
{/* Email Field */}
<div>
<label>Email:</label>
<input
{...register("email", {
required: "Email is required",
pattern: {
value: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
message: "Invalid email address",
},
})}
placeholder="Enter your email"
/>
{errors.email && <p>{errors.email.message}</p>}
</div>
{/* Password Field */}
<div>
<label>Password:</label>
<input
type="password"
{...register("password", {
required: "Password is required",
minLength: {
value: 6,
message: "Password must be at least 6 characters long",
},
})}
placeholder="Enter your password"
/>
{errors.password && <p>{errors.password.message}</p>}
</div>
<button type="submit" disabled={mutation.isLoading}>
{mutation.isLoading ? "Submitting..." : "Register"}
</button>
</form>
{mutation.isError && <p>An error occurred: {mutation.error.message}</p>}
{mutation.isSuccess && <p>Registration successful!</p>}
</div>
);
};
export default App;
4. Breakdown of the Code
React Hook Form:
useForm()
is used to manage form data.register
is used to connect form inputs to React Hook Form.handleSubmit
is a handler to manage form submission.errors
is used to display error messages when form validation fails.
TanStack Query (
useMutation
):useMutation
is used for submitting data (usually POST, PUT, DELETE).mutationFn
specifies the function that sends data to the backend (here it's using Axios to POST data).onSuccess
andonError
handle what happens when the request succeeds or fails.
5. Setting Up the Backend (Example)
Here's an example of a simple Express.js backend for receiving the registration data.
// server.js (Node.js/Express backend)
const express = require('express');
const app = express();
const cors = require('cors');
app.use(cors());
app.use(express.json());
app.post('/register', (req, res) => {
const { name, email, password } = req.body;
// Here, you'd save the user to the database and return a response
console.log('Received registration data:', { name, email, password });
res.status(201).json({ message: 'User registered successfully!' });
});
app.listen(4000, () => {
console.log('Server running on port 4000');
});
6. Testing the Full Flow
Run the backend server using
node server.js
.Run the React frontend using
npm start
.Fill out the form and submit it. You should see a POST request sent to the backend, and upon success, you'll see a "Registration successful!" message.
This example demonstrates how to build a simple registration form using React Hook Form for form handling and TanStack Query for submitting data to the backend, making it an efficient way to handle form submissions and API requests.
Advanced Password Matching Pattern-
To validate the "Password" and "Confirm Password" fields in React Hook Form, you can use custom validation rules. The goal is to ensure that the "Confirm Password" matches the "Password."
Here’s how you can do it:
Use the
validate
option within theregister
function for the "Confirm Password" input.Compare the value of the "Confirm Password" field with the value of the "Password" field during validation.
Here's how to implement this:
Updated App.js
Code with Password Confirmation Validation
// src/App.js
import React from 'react';
import { useForm } from 'react-hook-form';
import axios from 'axios';
import { useMutation } from '@tanstack/react-query';
const App = () => {
const { register, handleSubmit, watch, formState: { errors } } = useForm();
// Watch the value of the password input to compare with confirm password
const password = watch("password");
// Define the mutation using React Query's `useMutation`
const mutation = useMutation({
mutationFn: async (formData) => {
const response = await axios.post('https://your-backend-api.com/register', formData);
return response.data;
},
onSuccess: (data) => {
console.log("Registration Successful", data);
},
onError: (error) => {
console.log("Error Occurred", error);
}
});
// Handle form submission
const onSubmit = (data) => {
mutation.mutate(data); // Submit the form data via the mutation
};
return (
<div>
<h1>Register</h1>
<form onSubmit={handleSubmit(onSubmit)}>
{/* Name Field */}
<div>
<label>Name:</label>
<input
{...register("name", { required: "Name is required" })}
placeholder="Enter your name"
/>
{errors.name && <p>{errors.name.message}</p>}
</div>
{/* Email Field */}
<div>
<label>Email:</label>
<input
{...register("email", {
required: "Email is required",
pattern: {
value: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
message: "Invalid email address",
},
})}
placeholder="Enter your email"
/>
{errors.email && <p>{errors.email.message}</p>}
</div>
{/* Password Field */}
<div>
<label>Password:</label>
<input
type="password"
{...register("password", {
required: "Password is required",
minLength: {
value: 6,
message: "Password must be at least 6 characters long",
},
})}
placeholder="Enter your password"
/>
{errors.password && <p>{errors.password.message}</p>}
</div>
{/* Confirm Password Field */}
<div>
<label>Confirm Password:</label>
<input
type="password"
{...register("confirmPassword", {
required: "Please confirm your password",
validate: (value) => {
const password = watch("password");
if (!value || value !== password) {
return "passwords do not match"
} else {
return "Password matched!"
}
}
})}
placeholder="Confirm your password"
/>
{errors.confirmPassword && <p>{errors.confirmPassword.message}</p>}
</div>
<button type="submit" disabled={mutation.isLoading}>
{mutation.isLoading ? "Submitting..." : "Register"}
</button>
</form>
{mutation.isError && <p>An error occurred: {mutation.error.message}</p>}
{mutation.isSuccess && <p>Registration successful!</p>}
</div>
);
};
export default App;
Explanation of the Code
watch
function: Thewatch
function from React Hook Form is used to observe the value of the "Password" field, so we can compare it against the "Confirm Password" field.const password = watch("password");
validate
option: In theregister
function for the "Confirm Password" input, we use thevalidate
option to ensure that the value of "Confirm Password" matches the value of "Password."validate: (value) => { const password = watch("password"); if (!value || value !== password) { return "passwords do not match" } else { return "Password matched!" } }
If the two values don’t match, it returns the custom error message
"Passwords do not match"
.Error handling: If there is a validation error, it will show the error message using
errors.confirmPassword
:{errors.confirmPassword && <p>{errors.confirmPassword.message}</p>}
Testing the Flow
Try filling in different passwords in the "Password" and "Confirm Password" fields.
If the passwords don’t match, you'll see the error message "Passwords do not match."
If the passwords match, the form will submit successfully, and React Query will handle the API request.
This is how you can validate the "Password" and "Confirm Password" fields using React Hook Form in a form submission workflow with TanStack Query.