As I've been self studying React.js and recently learned about Custom Hooks, I decided it was time for a post about it!
In this example, I'll be showing step-by-step how to create some simple form validation, with error handling using Custom Hooks.
I will be working with three files. App.js
, Form.js
and input-hook.js
Setup
Project Structure:
📦src
┣ 📂components
┃ ┣ 📜Form.js
┣ 📂hooks
┃ ┣ 📜input-hook.js
┣ 📜App.js
┣ 📜index.css
┗ 📜index.js
App.js
Here we'll just render the form, nothing else.
import Form from "./components/Form";
function App() {
return (
<div className="app">
<Form />
</div>
);
}
export default App;
input-hook.js
import React, { useState } from "react";
const useInput = (validateValue) => {
const [inputValue, setInputValue] = useState("");
const [inputTouched, setInputTouched] = useState(false);
const isValid = validateValue(inputValue);
const hasError = !isValid && inputTouched;
const inputChangeHandler = (e) => {
setInputValue(e.target.value);
};
const inputBlurHandler = (e) => {
setInputTouched(true);
};
const reset = () => {
setInputValue("");
setInputTouched(false)
};
return {
value: inputValue,
isValid,
hasError,
inputChangeHandler,
inputBlurHandler,
reset,
};
};
export default useInput;
First, we import useState, as we will need it in our custom hook.
The useInput
custom component starts by using the useState
hook to create three state variables: value
, isInputValid
, and hasError
. We also included a argument validationFn
, which is where we will pass the logic for how to validate the field, from our Form.js.
value
- Keep track of what the user is typing
isInputValid
- Checks validity of whatever is typed
hasError
- A Boolean tracking both if the user started typing, and if what they typed is valid or not.
The first one inputChangeHandler
is passed to the input's onChange
prop. It updates the value state by checking the input's value.
The second one inputBlurHandler
is passed to the input's onBlur
prop. The handler checks if the user started typing or not.
The third one reset
is the function we use to reset the form to it's initial state.
Finally, we return all the properties we need to use in our Form.js
as an object.
Form.js
Form Structure
return (
<form onSubmit={submitHandler}>
<div className="control-group">
<div className={nameInputClasses}>
<label htmlFor="name">First Name</label>
<input
value={nameInputValue}
onBlur={nameBlurHandler}
onChange={nameChangeHandler}
type="text"
id="name"
/>
{nameHasError && <p className="error-text">Enter a name.</p>}
</div>
<div className={lastNameInputClasses}>
<label htmlFor="lastname">Last Name</label>
<input
value={lastNameInputValue}
onChange={lastNameChangeHandler}
onBlur={lastNameBlurHandler}
type="text"
id="lastname"
/>
{lastNameHasError && <p className="error-text">Enter a last name.</p>}
</div>
</div>
<div className={emailInputClasses}>
<label htmlFor="email">E-Mail Address</label>
<input
value={emailInputValue}
onChange={emailChangeHandler}
onBlur={emailBlurHandler}
type="text"
id="email"
/>
{emailHasError && <p className="error-text">Enter a real email.</p>}
</div>
<button type="submit" disabled={!formIsValid}>
Submit
</button>
</form>
);
};
export default Form;
In this component, there will be a lot going on. I'm going to break it down piece by piece.
First, we import the necessary dependencies: useInput
hook, React
, and useEffect
and useState
.
import useInput from "../hooks/input-hook";
import React, { useEffect, useState } from "react";
We then use the useState
hook to create a state variable formIsValid
which we set to false initially. It is used to check if our form is ready to be submitted or not.
const [formIsValid, setFormIsValid] = useState(false);
`
Next we are using our useInput
Custom Hook to create three new objects. One which checks name
, lastName
and lastly email
.
In our useInput
's argument, we pass a anonymous function. In this situation, we use that as an opportunity to check the input.
For the name fields, I make sure it's not empty, and for the email field I use RegEx to make sure it's a valid email format(it's not perfect, but works).
`
const {
value: nameInputValue,
isInputValid: isNameValid,
hasError: nameHasError,
inputChangeHandler: nameChangeHandler,
inputBlurHandler: nameBlurHandler,
reset: nameReset,
} = useInput((name) => name.trim() !== "");
const {
value: lastNameInputValue,
isInputValid: isLastNameValid,
hasError: lastNameHasError,
inputChangeHandler: lastNameChangeHandler,
inputBlurHandler: lastNameBlurHandler,
reset: lastNameReset,
} = useInput((lastName) => lastName.trim() !== "");
const {
value: emailInputValue,
isInputValid: isEmailValid,
hasError: emailHasError,
inputChangeHandler: emailChangeHandler,
inputBlurHandler: emailBlurHandler,
reset: emailReset,
} = useInput((email) =>
email.trim().match(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/)
);
Then, we use useEffect
to check if the inputs have a value, and if so set the form validity to true.
useEffect(() => {
if (nameInputValue && lastNameInputValue && emailInputValue) {
setFormIsValid(true);
} else {
setFormIsValid(false);
}
}, [nameInputValue, lastNameInputValue, emailInputValue]);
Next is the submitHandler
which gets called when the form is submitted. Basically what it does is:
- Prevents a refresh on submit
- Check that the form is valid
- If valid, submits and resets the input fields. Otherwise just return(do nothing).
const submitHandler = (event) => {
event.preventDefault();
if (!formIsValid) {
return;
}
nameReset();
lastNameReset();
emailReset();
};
Now, the next step is optional, it's basically playing with CSS to add classes with colors depending on the forms validity.
const nameInputClasses = nameHasError
? "form-control invalid"
: "form-control";
const lastNameInputClasses = lastNameHasError
? "form-control invalid"
: "form-control";
const emailInputClasses = emailHasError
? "form-control invalid"
: "form-control";
Check the objects value and if it has any error, change class to form-control invalid
.
And that's it!
Here's an example of the result:
Top comments (1)
Why create one when you can get all awesome hooks in a single library?
Try scriptkavi/hooks. Copy paste style and easy to integrate with its own CLI