Form Submission and Loading States
In this page:
const [submitting, setSubmitting] = useState(false);
async function handleSubmit(e) {
e.preventDefault();
setSubmitting(true);
try {
// await the submission
} finally {
setSubmitting(false);
}
}
<button disabled={submitting}>Submit</button>
Tracking Submission State
A boolean state like submitting tracks whether the form is currently being processed. Setting it to true right when submission starts, and back to false once it finishes, lets the UI show a loading indicator and disable the button during that window.
Example: Tracking Submission State
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [submitting, setSubmitting] = React.useState(false);
const handleSubmit = e => {
e.preventDefault();
setSubmitting(true);
setTimeout(() => setSubmitting(false), 1000);
};
return <form onSubmit={handleSubmit}><button disabled={submitting}>{submitting ? "Submitting..." : "Submit"}</button></form>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Handling Success and Error Outcomes
A real submission can succeed or fail, so it's useful to track a result state (like idle, success, or error) in addition to the loading flag. This lets the UI show the right message once the request finishes, instead of just silently resetting.
Example: Handling Success and Error Outcomes
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [status, setStatus] = React.useState("idle");
const handleSubmit = e => {
e.preventDefault();
setStatus("submitting");
setTimeout(() => setStatus("success"), 800);
};
return (
<form onSubmit={handleSubmit}>
<button disabled={status === "submitting"}>Submit</button>
{status === "success" && <p>Saved successfully!</p>}
</form>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Using try/catch/finally for Reliable Cleanup
Wrapping the async submission logic in try/catch/finally guarantees the loading state resets no matter what happens — whether the request succeeds, throws an error, or the network fails.
The finally block always runs, making it the safest place to turn off the loading flag.
Example: Using try/catch/finally for Reliable Cleanup
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [submitting, setSubmitting] = React.useState(false);
const [error, setError] = React.useState("");
const handleSubmit = async e => {
e.preventDefault();
setSubmitting(true);
try { throw new Error("Simulated failure"); }
catch (err) { setError(err.message); }
finally { setSubmitting(false); }
};
return <form onSubmit={handleSubmit}><button disabled={submitting}>Submit</button>{error && <p>{error}</p>}</form>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Not disabling the submit button while a submission is in progress, allowing duplicate submits.
- Forgetting to reset the loading state in a catch block, leaving the button stuck disabled forever after an error.
- Not showing any success or error feedback after the submission finishes.
- A submitting state (boolean) tracks whether a form request is currently in flight.
- The submit button should be disabled while submitting to prevent duplicate submissions.
- try/catch/finally ensures the loading state resets whether the submission succeeds or fails.
- Showing distinct success and error messages gives the user clear feedback on the outcome.
No React-version restriction — built on standard async/await and useState.
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: