Last active
June 11, 2022 19:33
-
-
Save mlh758/b133882b70f5a7c5ccdae3a61be82a19 to your computer and use it in GitHub Desktop.
Uncontrolled Forms Blog Post
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useRef, useEffect } from "react"; | |
interface Option { | |
name: string; | |
label: string; | |
} | |
interface CheckboxesProps { | |
options: Option[]; | |
} | |
export const Checkboxes: React.VFC<CheckboxesProps> = ({ options }) => { | |
// Store a ref to a container element to make it easy to select the checkboxes | |
const parentEl = useRef<HTMLFieldSetElement>(null); | |
// Use that ref to grab all the checkboxes. | |
const checkboxes = () => (parentEl.current?.querySelectorAll('input[type="checkbox"]') ?? []) as NodeListOf<HTMLInputElement>; | |
// setCustomValidity with a non-empty string marks the element as invalid. The message will be shown if you try to submit the form | |
const setAllInvalid = (nodes: NodeListOf<HTMLInputElement>) => { | |
nodes.forEach(checkbox => checkbox.setCustomValidity("You must select at least one option")); | |
} | |
// setCustomValidity with an empty string marks the element as valid | |
const setAllValid = (nodes: NodeListOf<HTMLInputElement>) => { | |
nodes.forEach(checkbox => checkbox.setCustomValidity("")); | |
} | |
// When any checkbox changes, see if any are checked and set the validity | |
const handleChange = () => { | |
let anyChecked = false; | |
const boxes = checkboxes(); | |
boxes.forEach(checkbox => anyChecked = anyChecked || checkbox.checked); | |
anyChecked ? setAllValid(boxes) : setAllInvalid(boxes); | |
} | |
useEffect(() => { | |
setAllInvalid(checkboxes()) | |
}, []) | |
return ( | |
<fieldset ref={parentEl}> | |
<legend>Select at least one</legend> | |
{options.map((opt) => ( | |
<label key={opt.name}> | |
<span>{opt.label}</span> | |
<input type="checkbox" name={opt.name} onChange={handleChange}/> | |
</label> | |
))} | |
</fieldset> | |
); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useCallback } from "react"; | |
import { makeStyles, FormHelperText } from "@material-ui/core"; | |
import { Checkboxes } from "./Checkboxes"; | |
// Just some basic styling to keep the form flowing top to bottom | |
// with a little spacing. | |
const useStyles = makeStyles((theme) => ({ | |
validatedForm: { | |
"& label": { | |
display: "block", | |
marginBottom: theme.spacing(2), | |
"& > span": { | |
marginRight: theme.spacing(1), | |
}, | |
}, | |
"& *:invalid": { | |
boxShadow: `0 0 5px 1px ${theme.palette.error.main}`, | |
}, | |
"& *:focus:invalid": { | |
boxShadow: "none", | |
}, | |
"& *:valid ~ p": { | |
display: "none", | |
}, | |
}, | |
})); | |
const checkboxOptions = [ | |
{ | |
label: "I consent to receiving marketing communications", | |
name: "consents", | |
}, | |
{ | |
label: "I do not consent to opting out of marketing communications", | |
name: "stillConsents", | |
}, | |
]; | |
export const Form = () => { | |
const handleSubmit = useCallback((e: React.FormEvent) => { | |
e.preventDefault(); | |
const formData = new FormData(e.target as HTMLFormElement); | |
formData.forEach((val, key) => console.log(`${key} => ${val}`)); | |
}, []); | |
const styles = useStyles(); | |
return ( | |
<div> | |
<h1>Give me all your personal information</h1> | |
<form onSubmit={handleSubmit} className={styles.validatedForm}> | |
<label> | |
<span>Legal Name</span> | |
<input name="fullname" /> | |
<FormHelperText>Enter your full name</FormHelperText> | |
</label> | |
<label> | |
<span>Date of Birth</span> | |
<input type="date" name="dob" required /> | |
<FormHelperText>Enter your date of birth</FormHelperText> | |
</label> | |
<label> | |
<span>Tell us about yourself</span> | |
<textarea minLength={20} maxLength={120} required /> | |
<FormHelperText>Use 20-120 characters to tell us about yourself</FormHelperText> | |
</label> | |
<Checkboxes options={checkboxOptions} /> | |
<button type="submit">Submit</button> | |
</form> | |
</div> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment