"forms, ughh.. I don't like it"
We all have been there. You might have tried several libraries like formik or redux forms to ease out your pain. Although decent solutions, you often get plenty of boilerplate code, which makes your code look less pretty and difficult to deal with for beginners. If you don't need the robustness of those libraries you can build your own custom form hook within a few lines of code.
This tutorial would require you to have some knowledge of modern react with hooks.
Step 1 : Initialize an empty create-react-app
Open the terminal and type:
npx create-react-app react-hook-form
Step 2: Open the project in your code editor.
Go to the src directory and create a new directory with the name hooks. Inside the hooks directory create a useForm.ts file (useForm.js if you're using javascript)
Step 3: Importing dependencies
For the custom hook, we will import useState
hook from "react"
.
import { useState } from "react";
Step 4: Defining the functional component.
Create a functional component named useForm
and define the initial state for the form. Don't forget to add the export
keyword.
import { useState } from "react";
// useForm functional component
export const useForm = (callback: any, initialState = {}) => {
const [values, setValues] = useState(initialState);
}
Here, initialState
will store the various values a form can have i.e. email, password, etc. which will be passed on from the component that uses this hook. callback
is the function that will be executed when the user submits the form.
Step 5: onChange function
onChange function is used to handle change events whenever a user types something in the input field.
import { useState } from "react";
// useForm functional componen
export const useForm = (callback: any, initialState = {}) => {
const [values, setValues] = useState(initialState);
// onChange
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValues({ ...values, [event.target.name]:
event.target.value });
};
}
This function sets the target value of the event that is passed to the target name. Suppose you gave an input element a name as "email", the value that is entered in the email field will be set to the email attribute in the initialState. This requires the initialState to have the attributes with the same name as the name specified in the input field.
Step 6: onSubmit function
onSubmit()
executes the callback()
function that was passed on when the user clicked the submit button.
import { useState } from "react";
// useForm functional componen
export const useForm = (callback: any, initialState = {}) => {
const [values, setValues] = useState(initialState);
// onChange
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValues({ ...values, [event.target.name]: event.target.value });
};
}
// onSubmit
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
await callback(); // triggering the callback
};
Your callback()
would usually be an asynchronous function like submitting login data to the database, so we use the await
keyword and define the onSubmit as an async function.
Step 7: Returning the hook's functions and data.
import { useState } from "react";
// useForm functional componen
export const useForm = (callback: any, initialState = {}) => {
const [values, setValues] = useState(initialState);
// onChange
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValues({ ...values, [event.target.name]: event.target.value });
};
}
// onSubmit
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
await callback(); // triggering the callback
};
// return values
return {
onChange,
onSubmit,
values,
};
We return the onChange
, onSubmit
and values
from this hook to the components that use this hook.
Step 8: Usage
Create a login.tsx file (login.jsx for javascript) in the src directory.
Add the following code.
import React, { useState } from "react";
import { useForm } from "./useForm";
function Login() {
// defining the initial state for the form
const initialState = {
email: "",
password: "",
};
// getting the event handlers from our custom hook
const { onChange, onSubmit, values } = useForm(
loginUserCallback,
initialState
);
// a submit function that will execute upon form submission
async function loginUserCallback() {
// send "values" to database
}
return (
// don't mind this ugly form :P
<form onSubmit={onSubmit}>
<div>
<input
name='email'
id='email'
type='email'
placeholder='Email'
onChange={onChange}
required
/>
<input
name='password'
id='password'
type='password'
placeholder='Password'
onChange={onChange}
required
/>
<button type='submit'>Login</button>
</div>
</form>
);
}
export default Login;
DONE! No bulky form components, add more event handlers to your custom hook to make it more robust. Easy and simple.
There is an npm package called react-hook-form which is gaining popularity. This tutorial is a basic insight into that package. Try adding more features like form validation to this custom hook of yours and make form building using react a better place :)
Thank you for reading! Any feedback/questions would be appreciated.
Top comments (5)
Thank you this is nice. To have full TypeScript support in your Form use this hook declaration:
Great post! Thank you for sharing!
This is good. Thank you.
Thank you for this. Well explained and, for a beginner, easy to follow.
Love it!
Will be a great start for my new form.