React Form Validation using React Hooks.


Vince Llauderes
import { useState, useEffect, useCallback } from 'react';
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 button
useEffect(() => {
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 component
const validateState = useCallback(() => {
const hasErrorInState = Object.keys(validationSchema).some(key => {
const isInputFieldRequired = validationSchema[key].required;
const stateValue = state[key].value; // state value
const stateError = state[key].error; // state error
return (isInputFieldRequired && !stateValue)
stateError;
});
return hasErrorInState;
}, [state, validationSchema]);
// Used to handle every changes in every input
const 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;
  // Define your state schema
const 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));
}
return { state, disable, handleOnChange, handleOnSubmit };
import React from 'react';
import useForm from './useForm';
function Form() {
// Define your state schema
const 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:
<input
type="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:
<input
type="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:
<input
type="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;
  // Define your state schema
const 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
);
return (
<div>
<form onSubmit={handleOnSubmit}>
<div>
<label htmlFor="fname">
First name:
<input
type="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:
<input
type="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:
<input
type="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>
);
}
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;
Don't forget to share

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *