React: Form Example
In React when we say a form input is "Controlled" this means that the value of the input is controlled by the state of the component. This is in contrast to an "Uncontrolled" input where the value is controlled by the DOM.
Inside of my <form>
I always like to wrap the entire contents with a fieldset. this way I can disable the entire form easily by setting the disabled attribute on the fieldset.
<fieldset disabled={loading} aria-busy={loading}>
first we need to create some states:
const [form, setForm] = useState(formInitialState);
const [showConfirm, setShowConfirm] = useState(false);
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [fail, setfail] = useState(false);
Lets create a handleReset, handleSetForm, and a submit function:
const handleReset = () => {
setForm(formInitialState);
setfail(false);
setSuccess(false);
};
const handleSetForm = ({ target: { name, value } }) => {
setForm((form) => ({ ...form, [name]: value }));
};
const confirm = (e) => {
e.preventDefault();
setShowConfirm(true);
};
const submit = async (e) => {
e.preventDefault();
setLoading(true);
console.log({ form });
// simulate send api request
await timeout(parseInt(form.timeout, 10), form.resolve)
.then((res) => {
handleReset();
confetti();
console.log(res);
})
.catch((res) => {
console.log("error");
setfail(true);
setSuccess(false);
console.log(res);
})
.finally(() => {
setLoading(false);
setShowConfirm(false);
});
};
and now for our form. here we will give each input a name this name
is used in our handleSetForm to know what input to update. we also give each input a value this value
is used to set the value of the input. we also give each input an onChange
handler this is used to update the state of the form. Finally on our form we give it an onSubmit
handler this is used to submit the form.
<form
onSubmit={confirm}
onReset={handleReset}
className={cn(success && "success", fail && "fail")}
>
<fieldset disabled={loading} aria-busy={loading}>
<input
placeholder="first"
value={form.first}
name="first"
onChange={handleSetForm}
/>
<input
placeholder="last"
value={form.last}
name="last"
onChange={handleSetForm}
/>
<input
placeholder="email"
value={form.email}
name="email"
onChange={handleSetForm}
/>
<div className="input-group">
<label htmlFor="timeout">timeout (ms):</label>
<input
id="timeout"
placeholder="timeout"
value={form.timeout}
name="timeout"
onChange={handleSetForm}
type="number"
/>
</div>
<select
placeholder="resolve"
value={form.resolve}
name="resolve"
onChange={handleSetForm}
>
<option value={false}>resolve</option>
<option value={true}>reject</option>
</select>
<div>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</div>
</fieldset>
</form>
Validation
Look into using zod to validate data from your forms. This will allow you to validate the data before sending it to the server: zod.dev
A great way to keep this organized is with conform: conform.guide
There are also other libraries like react-hook-form, formik, and final-form that can help with validation.