React Form Validation Using React Hooks Photo by on Efe Kurnaz Unsplash updates introduce a new feature in their library called . Hooks is one of the most revolutionary updates happened in React library. It gives you a lot more flexibility in writing stateful components w/out writing classes. There’s a saying “ ” you’ll avoid much boilerplate code in your React application. React 16.8 Hooks Write less, Do more So in this article I’m going to share with you on how I created my own form validation using React hooks. Before we get started, the complete repository can be downloaded in my . Feel free to modify the code if you have something to share or comments on it Enjoy! GitHub profile Okay so first let us create a file called in this file we should place our form validation hooks for separation of concerns. We follow the convention in React library that every functional component that uses hooks starts with keyword followed by their purpose, to easily determine what they can do. useForm.js “use” In this file, let’s import first all the hooks that we needed in React library. import { useState, useEffect, useCallback } from 'react'; After that let’s create a function called and has 3 parameters. useForm import { useState, useEffect, useCallback } from 'react'; function useForm(stateSchema, validationSchema = {}, callback) {const [state, setState] = useState(stateSchema);const [disable, setDisable] = useState(true);const [isDirty, setIsDirty] = useState(false); // Disable button in initial render.useEffect(() => {setDisable(true);}, []); // For every changed in our state this will be fired// To be able to disable the buttonuseEffect(() => {if (isDirty) {setDisable(validateState());}}, [state, isDirty]); // Used to disable submit button if there's an error in state// or the required field in state has no value.// Wrapped in useCallback to cached the function to avoid intensive memory leaked// in every re-render in componentconst validateState = useCallback(() => {const hasErrorInState = Object.keys(validationSchema).some(key => {const isInputFieldRequired = validationSchema[key].required;const stateValue = state[key].value; // state valueconst stateError = state[key].error; // state error return (isInputFieldRequired && !stateValue) || stateError; }); return hasErrorInState; }, [state, validationSchema]); // Used to handle every changes in every inputconst handleOnChange = useCallback(event => {setIsDirty(true); const name = event.target.name; const value = event.target.value; let error = ''; if (validationSchema\[name\].required) { if (!value) { error = 'This is required field.'; } } if ( validationSchema\[name\].validator !== null && typeof validationSchema\[name\].validator === 'object' ) { if (value && !validationSchema\[name\].validator.regEx.test(value)) { error = validationSchema\[name\].validator.error; } } setState(prevState => ({ ...prevState, \[name\]: { value, error }, })); }, \[validationSchema\] ); const handleOnSubmit = useCallback(event => {event.preventDefault(); // Make sure that validateState returns false // Before calling the submit callback function if (!validateState()) { callback(state); } }, \[state\] ); return { state, disable, handleOnChange, handleOnSubmit };} export default useForm; The first parameter in our useForm function is . In this parameter, we should define our state in our form. stateSchema Example: // Define your state schemaconst stateSchema = {fname: { value: '', error: '' },lname: { value: '', error: '' },tags: { value: '', error: '' },}; Our object consist all the fields in our form. As you can see here we have an and has object value containing the property of and The value is the value on what we type in our input field and the error is the error in our input field if there’s any. stateSchema fname value error. The second parameter is . In this parameter we define our own rules in validating our state in our form. validationSchema Example: // Define your validationStateSchema// Note: validationStateSchema and stateSchema property// should be the same in-order validation works!const validationStateSchema = {fname: {required: true,validator: {regEx: /^[a-zA-Z]+$/,error: 'Invalid first name format.',},},lname: {required: true,validator: {regEx: /^[a-zA-Z]+$/,error: 'Invalid last name format.',},},tags: {required: true,validator: {regEx: /^(,?\w{3,})+$/,error: 'Invalid tag format.',},},}; As you’ve noticed here property and property is the same. Because we need to map the validation in stateSchema to validate fields that we define in stateSchema (eg: fname, lname and tags etc). stateSchema validationSchema The third parameter is callback function that we execute if we submit the form. Usually we placed here the api for handling form submission. function onSubmitForm(state) {alert(JSON.stringify(state, null, 2));} Our useForm function returns values such as ( ) state, disable, handleOnChange, handleOnSubmit return { state, disable, handleOnChange, handleOnSubmit }; returns an object of current state in our form. state returns a boolean to be able to disable the submit button if there’s an error in the state disable returns a function to set the values in our form state. handleOnChange returns a function to submit the values in our form. handleOnSubmit So we’re now done creating our useForm hooks and we can now hook it in our Component. Before that let’s create first our Form component. import React from 'react';import useForm from './useForm'; function Form() {// Define your state schemaconst stateSchema = {fname: { value: '', error: '' },lname: { value: '', error: '' },tags: { value: '', error: '' },}; // Define your validationStateSchema// Note: validationStateSchema and stateSchema property// should be the same in-order validation works!const validationStateSchema = {fname: {required: true,validator: {regEx: /^[a-zA-Z]+$/,error: 'Invalid first name format.',},},lname: {required: true,validator: {regEx: /^[a-zA-Z]+$/,error: 'Invalid last name format.',},},tags: {required: true,validator: {regEx: /^(,?\w{3,})+$/,error: 'Invalid tag format.',},},}; function onSubmitForm(state) {alert(JSON.stringify(state, null, 2));} const { state, handleOnChange, handleOnSubmit, disable } = useForm(stateSchema,validationStateSchema,onSubmitForm); const errorStyle = {color: 'red',fontSize: '13px',}; return (<div><form onSubmit={handleOnSubmit}><div><label htmlFor="fname">First name:<inputtype="text"name="fname"value={state.fname.value}onChange={handleOnChange}/></label>{state.fname.error && <p style={errorStyle}>{state.fname.error}</p>}</div> <div><label htmlFor="lname">Last name:<inputtype="text"name="lname"value={state.lname.value}onChange={handleOnChange}/></label>{state.lname.error && <p style={errorStyle}>{state.lname.error}</p>}</div> <div><label htmlFor="tags">Tags:<inputtype="text"name="tags"value={state.tags.value}onChange={handleOnChange}/></label>{state.tags.error && <p style={errorStyle}>{state.tags.error}</p>}</div> <input type="submit" name="submit" disabled={disable} /></form></div>);} export default Form; In our Form component we used our previously created hooks called useForm and passed all the required argument to be able to use it. Our stateSchema: // Define your state schemaconst stateSchema = {fname: { value: '', error: '' },lname: { value: '', error: '' },tags: { value: '', error: '' },}; Our validationSchema: // Define your validationStateSchema// Note: validationStateSchema and stateSchema property// should be the same in-order validation works!const validationStateSchema = {fname: {required: true,validator: {regEx: /^[a-zA-Z]+$/,error: 'Invalid first name format.',},},lname: {required: true,validator: {regEx: /^[a-zA-Z]+$/,error: 'Invalid last name format.',},},tags: {required: true,validator: {regEx: /^(,?\w{3,})+$/,error: 'Invalid tag format.',},},}; Our callback function in submitting form: function onSubmitForm(state) {alert(JSON.stringify(state, null, 2));} Passed all the required argument in useForm to use it. const { state, handleOnChange, handleOnSubmit, disable } = useForm(stateSchema,validationStateSchema,onSubmitForm); Remember our useForm hooks returns a values such as state, handleOnChange, handleOnSubmit, disable. With that we can now use it in our Form component. return (<div><form onSubmit={handleOnSubmit}><div><label htmlFor="fname">First name:<inputtype="text"name="fname"value={state.fname.value}onChange={handleOnChange}/></label>{state.fname.error && <p style={errorStyle}>{state.fname.error}</p>}</div> <div><label htmlFor="lname">Last name:<inputtype="text"name="lname"value={state.lname.value}onChange={handleOnChange}/></label>{state.lname.error && <p style={errorStyle}>{state.lname.error}</p>}</div> <div><label htmlFor="tags">Tags:<inputtype="text"name="tags"value={state.tags.value}onChange={handleOnChange}/></label>{state.tags.error && <p style={errorStyle}>{state.tags.error}</p>}</div> <input type="submit" name="submit" disabled={disable} /></form></div>);} After that let us import our Form component in App.js. import React from 'react';import Form from './Form'; import logo from './logo.svg';import './App.css'; function App() {return (<div className="App"><div className="App-header"><img className="App-logo" src={logo} alt="react-logo" /><p>React Form Validation using React Hooks.</p></div><Form /></div>);} export default App; Here’s the final output of our created useForm hooks with Form component. using useForm hooks in Form component Hope it helps 😃 If you enjoy reading this article give me a clapped 👏 👏 👏 Thank you. “Don’t be a JavaScript Mediocre.” Follow me on twitter https://twitter.com/llaudevc/