Part 63: Enhancing User Experience: Error Handling and Loading States in React Forms
[Pages] Authentication

In our journey to build a robust Sign-In form, we've successfully connected our form to an API, allowing users to log in with their credentials. However, until now, if a user entered incorrect credentials, the application did not provide any feedback, leading to a poor user experience. Today, we're going to address this by adding error handling and improving the user interface to manage loading states.
Handling API Request Errors
When a user submits the form with incorrect credentials, we want to display an informative error message. Let's implement this feature.
Step 1: Introduce a Status State
We'll add a state variable to manage both loading and error states.
// pages/sign-in.js
import { useState } from 'react';
import Button from '../components/Button';
import Field from '../components/Field';
import Input from '../components/Input';
import Page from '../components/Page';
import { fetchJson } from '../lib/api';
function SignInPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [status, setStatus] = useState({ loading: false, error: false });
const handleSubmit = async (event) => {
event.preventDefault();
setStatus({ loading: true, error: false });
try {
const response = await fetchJson('http://localhost:1337/auth/local', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ identifier: email, password }),
});
setStatus({ loading: false, error: false });
console.log('sign in:', response);
} catch (err) {
setStatus({ loading: false, error: true });
}
};
return (
<Page title="Sign In">
<form onSubmit={handleSubmit}>
<Field label="Email">
<Input type="email" required value={email}
onChange={(event) => setEmail(event.target.value)}
/>
</Field>
<Field label="Password">
<Input type="password" required value={password}
onChange={(event) => setPassword(event.target.value)}
/>
</Field>
{status.error && (
<p className="text-red-700">
Invalid credentials
</p>
)}
{status.loading ? (
<p>Loading...</p>
) : (
<Button type="submit">
Sign In
</Button>
)}
</form>
</Page>
);
}
export default SignInPage;Explanation:
Status State: We use a single
statusstate variable to track bothloadinganderrorconditions.Error Handling: We use a
try-catchblock to handle errors. If an error occurs,status.erroris set totrue.Conditional Rendering: We conditionally render an error message if
status.erroris true.
Managing Loading States
To prevent multiple form submissions while waiting for a response, we will disable the "Sign In" button during the loading phase.
Step 2: Implement Loading Indicator and Disable Button
Loading State: We set
status.loadingtotruewhen the request is in progress.Button Disabled: The "Sign In" button is replaced with a "Loading..." text during the request.
Explanation:
Loading Indicator: Display "Loading..." to inform users that a request is being processed.
Prevent Multiple Submissions: Disabling the button prevents users from submitting the form multiple times.
Simulating a Slow Network
To simulate a slow network response and test our loading indicator, we can artificially introduce a delay. This helps us ensure that the loading state is handled correctly.
Add a Sleep Function
// lib/api.js
export const sleep = (milliseconds) => {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
};
// Usage: await sleep(2000);Explanation:
Sleep Function: Converts
setTimeoutinto a Promise, allowing us toawaita delay. This simulates network latency during development.
Conclusion
With these enhancements, our Sign-In form provides clear feedback to users when errors occur and manages loading states effectively. This significantly improves the user experience by preventing multiple submissions and displaying error messages when necessary.
In our next step, we'll focus on handling the JSON web token received upon successful login and deciding how to store and use it within our application. Stay tuned for more improvements!
Last updated