Exercise 2: Form Handling and Validation
Objective
Master controlled components, form handling, and validation in React.
Task
Build a complete registration form with validation.
Requirements
1. Form Fields
- Username (3-20 characters, alphanumeric only)
- Email (valid email format)
- Password (min 8 chars, must include uppercase, lowercase, number, special char)
- Confirm Password (must match password)
- Date of Birth (must be 18+)
- Country (dropdown select)
- Terms and Conditions (checkbox, required)
2. Validation Rules
- Real-time validation on blur
- Show errors only after field is touched
- Disable submit if form is invalid
- Clear validation on field focus
- Display success message on valid submission
3. Features
- All fields controlled by React state
- Custom validation messages
- Show/hide password toggle
- Password strength indicator
- Form submission handling
- Reset form after submission
Implementation
interface FormData {
username: string;
email: string;
password: string;
confirmPassword: string;
dateOfBirth: string;
country: string;
acceptedTerms: boolean;
}
interface FormErrors {
[key: string]: string;
}
function RegistrationForm() {
// Your implementation here
}
Validation Functions
const validateUsername = (username: string): string => {
if (!username) return 'Username is required';
if (username.length < 3) return 'Username must be at least 3 characters';
if (username.length > 20) return 'Username must be less than 20 characters';
if (!/^[a-zA-Z0-9]+$/.test(username)) return 'Username must be alphanumeric';
return '';
};
const validateEmail = (email: string): string => {
if (!email) return 'Email is required';
if (!/\S+@\S+\.\S+/.test(email)) return 'Email is invalid';
return '';
};
// Implement remaining validators...
Password Strength Indicator
type PasswordStrength = 'weak' | 'medium' | 'strong';
const getPasswordStrength = (password: string): PasswordStrength => {
let strength = 0;
if (password.length >= 8) strength++;
if (/[a-z]/.test(password) && /[A-Z]/.test(password)) strength++;
if (/\d/.test(password)) strength++;
if (/[^a-zA-Z0-9]/.test(password)) strength++;
if (strength <= 1) return 'weak';
if (strength <= 3) return 'medium';
return 'strong';
};
Expected UI
function App() {
return (
<div className='container'>
<h1>Create Account</h1>
<RegistrationForm onSubmit={data => console.log(data)} />
</div>
);
}
Bonus Challenges
- Add async email availability check (simulate API call)
- Implement auto-focus on first error field
- Add "Sign in with Google" button (UI only)
- Save form progress to localStorage
- Add phone number field with country code selector
- Implement multi-step form (wizard)
Testing Checklist
- All validation rules work correctly
- Errors show only after field is touched
- Submit button is disabled when form is invalid
- Password strength indicator updates correctly
- Passwords must match
- Age validation works (must be 18+)
- Form resets after successful submission
- Terms checkbox is required
Time Estimate
3-4 hours
Hints
- Create a custom
useFormhook to manage state - Use a
touchedstate object to track which fields have been interacted with - Extract validation logic into separate functions
- Consider using a validation library for bonus (Yup, Zod)