Skip to content

Instantly share code, notes, and snippets.

@rafaelcamargo
Created December 20, 2024 18:45
Show Gist options
  • Save rafaelcamargo/ae7008f154056376f675191176d71020 to your computer and use it in GitHub Desktop.
Save rafaelcamargo/ae7008f154056376f675191176d71020 to your computer and use it in GitHub Desktop.
React Form Validation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Newsletter Form</title>
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.13.8/babel.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.production.min.js"></script>
<style>
html, body {
margin: 0;
background-color: #F7F7F7;
color: #808080;
font-size: 16px;
font-family: Arial, sans-serif;
}
html, body, .root {
width: 100vw;
height: 100vh;
}
.root {
display: flex;
align-items: center;
justify-content: center;
}
form {
width: 100%;
max-width: 400px;
}
form > div:not(:first-child) {
margin-top: 10px;
}
form input + span {
display: block;
margin-top: 5px;
color: #B31348;
font-size: .875rem;
}
input, button {
transition: all .3s ease-in-out;
}
input {
display: block;
width: 100%;
padding: 9.5px;
background-color: #DEE2ED;
color: #29303D;
border: 1px solid #F7F7F7;
border-radius: 8px;
box-shadow: 0 0 0 0 #DADEE7;
box-sizing: border-box;
font-size: 1.125rem;
outline: 0;
}
input:active,
input:focus {
box-shadow: 0 0 0 3px #DEE2ED;
}
button:active,
button:focus {
box-shadow: 0 0 0 3px #ADB9D2;
}
input::placeholder {
color: #9ca8c9;
}
button {
display: block;
margin: 20px auto 0;
padding: 0 20px;
width: 100%;
max-width: 200px;
height: 40px;
background-color: #242E42;
color: #EEF0F6;
font-size: 1.125rem;
text-decoration: none;
line-height: 1;
border: 0;
border-radius: 20px;
box-shadow: 0 0 0 0 #DADEE7;
box-sizing: border-box;
outline: 0;
-webkit-appearance: none;
}
button:hover {
cursor: pointer;
background-color: #485C84;
}
</style>
</head>
<body>
<div id="root" class="root"></div>
<script type="text/babel">
(function () {
const { useState } = React;
const { createRoot } = ReactDOM;
const NAME_FIELD_NAME = 'name';
const EMAIL_FIELD_NAME = 'email';
const NewsletterForm = () => {
const [formData, setFormData] = useState({});
const [hasSubmitted, setSubmission] = useState();
const validations = buildValidations();
const formErrors = validateForm(formData, validations);
const handleChange = ({ target: { name, value } }) => {
setFormData({ ...formData, [name]: value });
};
const buildErrorMessageEl = fieldName => {
const message = formErrors[fieldName];
return hasSubmitted && message && <span>{message}</span>;
}
const handleSubmit = evt => {
evt.preventDefault();
setSubmission(true);
if (Object.keys(formErrors).length === 0) {
alert('Form submitted!');
setSubmission(false);
setFormData({});
}
};
return (
<form onSubmit={handleSubmit} autocomplete="off">
<div>
<input
aria-label="Name"
placeholder="Name"
name={NAME_FIELD_NAME}
value={formData[NAME_FIELD_NAME] || ''}
onChange={handleChange}
/>
{buildErrorMessageEl(NAME_FIELD_NAME)}
</div>
<div>
<input
aria-label="Email"
placeholder="Email"
name={EMAIL_FIELD_NAME}
value={formData[EMAIL_FIELD_NAME] || ''}
onChange={handleChange}
/>
{buildErrorMessageEl(EMAIL_FIELD_NAME)}
</div>
<button type="submit">Subscribe</button>
</form>
);
};
function buildValidations(){
return {
[NAME_FIELD_NAME]: [{
isValid: val => !!val,
errorMessage: 'Required'
}, {
isValid: val => val?.length >= 2,
errorMessage: 'Enter at least 2 characters'
}],
[EMAIL_FIELD_NAME]: [{
isValid: val => !!val,
errorMessage: 'Required'
}, {
isValid: val => isEmailValid(val),
errorMessage: 'Enter a valid email address'
}]
};
}
// form-validator
function validateForm(formData, validations) {
return Object
.entries(validations)
.reduce((formErrors, [fieldName, fieldValidations]) => {
const err = validateField(formData[fieldName], fieldValidations);
return err ? { ...formErrors, [fieldName]: err } : formErrors;
}, {});
}
function validateField(fieldValue, fieldValidations){
return fieldValidations
.reduce((fieldError, { isValid, errorMessage }) => {
if(fieldError) return fieldError;
return !isValid(fieldValue) ? errorMessage : fieldError
}, '');
}
// email-validator
function isEmailValid(email){
const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return regex.test(email);
};
const root = createRoot(document.getElementById('root'));
root.render(<NewsletterForm />);
}())
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment