Introduction
In this article, you will learn how to use usestate for state management using a custom hook. Consume the custom hook using another component. Once data logic is validated, it will call the respective handler and update the states.
Why do we require UseState in React?
- Allows you to add a state to a functional component.
- It returns an array with two values: the current state and a function to update it.
- The Hook takes an initial state value as an argument and returns an updated state value whenever the setter function is called.
Why do we require a custom hook?
- Custom hooks provide a simple alternative to Higher Order Component and Render props.
- Custom hook provides less repetitive code.
- Custom hook also provides fewer keystrokes.
Topics Covered
This article demonstrates how to build the following,
- Define state in customhook using usestate.
- Create a sample project to consume the custom hook.
- Testing
Pre-requisites
- Download and install React 18.2.0.
Tools
- React 18.2.0
Task 1. Create a sample react-js application
In this task, you will see how to create a sample react-js application using visual studio code.
Step 1
Open visual studio code and run the command in the terminal.
Step 2
Once the project is created successfully.
Go inside the folder.
And run the project
Step 3
To check default project is running successfully. Consider the below screen.
Once the project runs successfully below screen will appear.
Task 2: Create a new folder like “CustomHook” and add a new component in it with the name ‘use-input-usestate.js’
In this task, you will see how to create a component inside a folder.
Step 1
Create a new folder inside src => with the name “CustomHook” and a component.
Don’t confuse my project and folder name. That’s why my project name is custohook (‘m’ Missing), and my folder name is ‘CustomHook’.
Step 2
Add a component in this folder with the name ‘use-input-usestate.js’. As per standard, when we create any custom hook, we always start the name using the ‘use’ keyword.
Step 3
Put the below code in this file; in the below code, we accept the control validation condition as an input parameter, and the output will be an object in which the component will pass two handlers. Those will be consumed from the child component.
valueChangedHandler,
inputBlurHandler
Events types will be ‘onChange’ and ‘onBlur’.
With few properties
value: enteredValue,
isValid: valueIsValid,
hasError,
These properties will validate the parent component controls value, and the Last property will be used to reset the control default value.
reset
import { useState } from "react";
export function useInput(validateValue) {
const [enteredValue, setEnteredValue] = useState("");
const [isTouched, setIsTouched] = useState(false);
const valueIsValid = validateValue(enteredValue);
const hasError = !validateValue && isTouched;
const valueChangedHandler = (event) => {
setEnteredValue(event.target.value);
};
const inputBlurHandler = () => {
setIsTouched(true);
};
const reset = () => {
setEnteredValue("");
setIsTouched(false);
};
return {
value: enteredValue,
isValid: valueIsValid,
hasError,
valueChangedHandler,
inputBlurHandler,
reset
};
}
Step 4
Create another component with the name “BaseForm”.
In which the user has two input fields, a custom component system can validate the input field response and showcase the proper error/success.
Note. I keep two input controls for my example, but it can be as many as you require.
Step 5
Add the below line of code to create the email and name input field with an error message; those will appear based on the conditions.
export function BaseForm() {
const handleSubmit = async (event) => {
event.preventDefault();
};
return (
<body>
<h3>Custom Hook</h3>
<table border='1px solid'>
<tr>
<td>Please enter the Email</td>
<td>
<input
id="email"
type="email"
></input>
</td>
</tr>
<tr>
<td colSpan='2'>
{!IsEmailValid && (
<p className="invalidInput">Please enter a valid email</p>
)}
</td>
</tr>
<tr>
<td>Please enter the Name</td>
<td>
<input
id="name"
type="text"
></input>
</td>
</tr>
<tr>
<td colSpan='2'>
Name can't be empty
</td>
</tr>
<tr>
<td colSpan="3">
<button className="submitBtn" onClick={handleSubmit}>
Submit
</button>
</td>
</tr>
</table>
</body>
);
}
Consume Custom Control
Step 1
Import the custom control in the ‘baseform’ control.
import { useInput } from "./CustomHook/use-input-usestate";
Step 2
Modify input controls and add their respective handlers who will call when users type any value in it.
The name of the control will be as per your requirement.
Step 3
Consume and call the custom control. Based on the input parameter logic, we will showcase the response.
- For Name- The user needs to enter the value, and before sending it to custom control, we are using the Trim function to remove the blank space.
- For Email- The user needs to enter an email, and we added an email validation logic before calling the custom control.
The complete code will be this.
import { useInput } from "./CustomHook/use-input";
export function BaseForm() {
const {
value: enterdName,
hasError: nameHasError,
isValid: IsNameValid,
valueChangedHandler: handleValueChange,
inputBlurHandler: handleBlueChange,
reset: handlerReset,
} = useInput((value) => value.trim() === "");
const {
value: enterdEmail,
hasError: emailHasError,
isValid: IsEmailValid,
valueChangedHandler: handleEmailChange,
inputBlurHandler: handleBlurEmailChange,
reset: handlerEmailReset,
} = useInput((value) => value.includes("@"));
const handleSubmit = async (event) => {
event.preventDefault();
console.log("Handle Submit Called");
console.log("Email value : " + event.target.email.value);
console.log("Name value : " + event.target.name.value);
handlerReset();
handlerEmailReset();
};
return (
<body>
<h3>Custom Hook</h3>
<form onSubmit={handleSubmit}>
<table border="1px solid">
<tr>
<td>Please enter the Email</td>
<td>
<input
id="email"
type="email"
onChange={handleEmailChange}
onBlur={handleBlurEmailChange}
value={enterdEmail}
></input>
</td>
</tr>
<tr>
<td colSpan="2">
{!IsEmailValid && (
<p className="invalidInput">Please enter a valid email</p>
)}
</td>
</tr>
<tr>
<td>Please enter the Name</td>
<td>
<input
id="name"
type="text"
onChange={handleValueChange}
onBlur={handleBlueChange}
value={enterdName}
></input>
</td>
</tr>
<tr>
<td colSpan="2">
{IsNameValid && (
<p className="invalidInput">Name can't be empty</p>
)}
</td>
</tr>
<tr>
<td colSpan="3">
<button className="submitBtn" >
Submit
</button>
</td>
</tr>
</table>
</form>
</body>
);
}
Output
Execute the code using.
npm start and then enter a valid email address and name.
Click on submit and consider the console output. It will automatically blank the value using the reset property method.
Now enter the invalid email and keep the name blank.
It will show the error.
Summary
So, we have seen how much we have minimized the code and reduced code repetition. This way, we can write custom hooks that prevent code repetition and provide code minimization. We can write custom hooks for many implementations like generating sliders, generalizing functions, or any task implementation.