794 lines
23 KiB
Plaintext
794 lines
23 KiB
Plaintext
---
|
|
title: Forms in React Interviews
|
|
description: Guide to building interactive React forms, covering controlled vs uncontrolled components, diverse input types, complex state management, and robust error handling and validation strategies
|
|
---
|
|
|
|
Forms in React are a crucial part of building interactive applications, allowing users to input and submit data. In React, it is common to control form elements using state, making them dynamic yet predictable.
|
|
|
|
## Controlled vs uncontrolled form components
|
|
|
|
In React, form inputs can be managed in two ways: **controlled** and **uncontrolled**. The main difference lies in how the form data is handled.
|
|
|
|
### Controlled form components
|
|
|
|
In a **controlled form component**, React manages the form element's state. The value of the input is stored in a state variable and updated via an `onChange` handler.
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function ControlledForm() {
|
|
const [name, setName] = useState('');
|
|
|
|
function handleChange(event) {
|
|
setName(event.target.value);
|
|
}
|
|
|
|
function handleSubmit(event) {
|
|
event.preventDefault();
|
|
alert(`Submitted Name: ${name}`);
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit}>
|
|
<label>
|
|
Name:
|
|
<input type="text" value={name} onChange={handleChange} />
|
|
</label>
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
In controlled components, the input value is stored in React state (`name`). The `onChange` handler updates the state as the user types. This ensures the value is always controlled by React.
|
|
|
|
### Uncontrolled form components
|
|
|
|
In an **uncontrolled form component**, the form element's value is managed by the DOM itself rather than React state.
|
|
|
|
```jsx
|
|
import { useRef } from 'react';
|
|
|
|
function UncontrolledForm() {
|
|
const nameRef = useRef();
|
|
|
|
function handleSubmit(event) {
|
|
event.preventDefault();
|
|
|
|
// Access form values using `FormData`
|
|
const formData = new FormData(event.target);
|
|
console.log('Name:', formData.get('name'));
|
|
|
|
// Alternatively, access the <input> via a ref
|
|
console.log('Name:', nameRef.current.value);
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit}>
|
|
<label>
|
|
Name:
|
|
<input type="text" name="name" ref={nameRef} />
|
|
</label>
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
In uncontrolled components, the input value is not stored in React state. To access the form data upon submission, we can either:
|
|
|
|
1. **Use `FormData`**: Access the form element directly from `event.target`, create a `FormData` instance from the form, and retrieve values using `formData.get('name)` (corresponds to the `name` attribute on `<input>`)
|
|
2. **Use refs**: Use `useRef()` to reference the `<input>` field directly. The input value can be accessed via `nameRef.current.value`
|
|
|
|
### When to use which?
|
|
|
|
| Feature | Controlled form | Uncontrolled form |
|
|
| --- | --- | --- |
|
|
| **Where state is stored** | React state (`useState`) | Native DOM |
|
|
| **Performance** | Re-rendering needed on update, might cause issues in large forms | More performant for simple use cases |
|
|
| **Validation** | Easy to implement | Requires manual validation |
|
|
| **Form reset** | Easy (`setState("")`) | Needs `ref.current.value = ""` or trigger a `'reset'` event |
|
|
| **Use case** | Dynamic forms, validation, real-time updates | Simple forms, file uploads, or integrating non-React code |
|
|
|
|
- Use **controlled form components** when you need to validate, manipulate, or track user input dynamically (e.g. toggling visibility of certain form fields based on previous responses) or nested form state
|
|
- Use **uncontrolled form components** when working with large forms, integrating with non-React code, or optimizing performance
|
|
|
|
In interviews, considering the forms usually do not have many fields, both controlled components and uncontrolled approaches are viable. Personally, we'd lean towards controlled components since you might be asked to reset the values, add validation-on-typing, etc and it'd be easier to implement.
|
|
|
|
## Handling different input types
|
|
|
|
The following are code examples for various input types using the controlled approach, meaning their values are stored in state and updated via `onChange` handlers.
|
|
|
|
### Text input
|
|
|
|
A controlled text input updates its value in state as the user types.
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function TextInputExample() {
|
|
const [text, setText] = useState('');
|
|
|
|
return (
|
|
<div>
|
|
<label htmlFor="name-input">Name</label>
|
|
<input
|
|
id="name-input"
|
|
type="text"
|
|
value={text}
|
|
onChange={(event) => setText(event.target.value)}
|
|
/>
|
|
<p>Entered Text: {text}</p>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
- The input field's value is controlled by React state (`text`)
|
|
- The `onChange` event updates state with `setText(event.target.value)`
|
|
- This ensures React manages the input value dynamically
|
|
|
|
There are many other text-based values for the `type` attribute, each with various purposes:
|
|
|
|
| `type` | Purpose | Built-in validation |
|
|
| --- | --- | --- |
|
|
| `text` | General text input | No |
|
|
| `number` | Numerical text input | Yes, only numbers allowed. Additional validation via `min`/`max` attributes |
|
|
| `email` | Email addresses | Yes, must contain `@` |
|
|
| `password` | Secure password input | No, but input is masked |
|
|
| `search` | Search field with a clear button | No |
|
|
| `tel` | Telephone numbers | No. Additional validation via `pattern` attribute |
|
|
| `url` | URLs | Yes, must start with `http://` or `https://` |
|
|
| `datetime-local` | Date and time selection | Yes |
|
|
| `color` | Color picker | Yes |
|
|
|
|
**Recommendations:**
|
|
|
|
- Use **`text`** for generic input fields
|
|
- Use **`number`** when you expect only numeric values and built-in validation for numbers
|
|
- Use **`email`**, **`url`**, and **`tel`** to take advantage of built-in validation
|
|
- Use **`password`** for secure text entry
|
|
- Use **`search`** for fields optimized for searching
|
|
- Use **`datetime-local`** when date & time selection is needed
|
|
- Use **`color`** for color selection
|
|
|
|
### Checkbox input
|
|
|
|
A checkbox is a **boolean value** (checked or unchecked).
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function CheckboxExample() {
|
|
const [isChecked, setIsChecked] = useState(false);
|
|
|
|
return (
|
|
<div>
|
|
<input
|
|
id="checkbox-input"
|
|
type="checkbox"
|
|
checked={isChecked}
|
|
onChange={(event) => setIsChecked(event.target.checked)}
|
|
/>
|
|
<label htmlFor="checkbox-input">Agree to terms and conditions</label>
|
|
<p>Checkbox is {isChecked ? 'checked' : 'unchecked'}</p>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
- The `checked` attribute is bound to `isChecked` state
|
|
- The `onChange` event updates state with `setIsChecked(event.target.checked)`
|
|
- This allows React to track the checkbox's checked status
|
|
|
|
### Radio group
|
|
|
|
Radio buttons are used when selecting **one option from multiple choices**.
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function RadioGroupExample() {
|
|
const [gender, setGender] = useState('');
|
|
|
|
return (
|
|
<div>
|
|
<div>
|
|
<input
|
|
id="radio-male"
|
|
type="radio"
|
|
name="gender"
|
|
value="male"
|
|
checked={gender === 'male'}
|
|
onChange={(event) => setGender(event.target.value)}
|
|
/>
|
|
<label htmlFor="radio-male">Male</label>
|
|
</div>
|
|
<div>
|
|
<input
|
|
id="radio-female"
|
|
type="radio"
|
|
name="gender"
|
|
value="female"
|
|
checked={gender === 'female'}
|
|
onChange={(event) => setGender(event.target.value)}
|
|
/>
|
|
<label htmlFor="radio-female">Female</label>
|
|
</div>
|
|
<p>Selected gender: {gender}</p>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
- The `name="gender"` ensures only one option can be selected
|
|
- The `checked` attribute checks if the current value matches the state (`gender === "male"` or `"female"`)
|
|
- The `onChange` updates state with the selected value
|
|
|
|
### Textarea
|
|
|
|
A `textarea` is used for multi-line text input.
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function TextAreaExample() {
|
|
const [bio, setBio] = useState('');
|
|
|
|
return (
|
|
<div>
|
|
<label htmlFor="bio-input">Bio:</label>
|
|
<textarea
|
|
id="bio-input"
|
|
value={bio}
|
|
onChange={(event) => setBio(event.target.value)}
|
|
/>
|
|
<p>Bio Preview: {bio}</p>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
- Unlike HTML, React uses `value` instead of setting text inside `<textarea>`
|
|
- The `onChange` updates the state with `setBio(event.target.value)`, ensuring React controls the input
|
|
|
|
### Select dropdown
|
|
|
|
A `<select>` dropdown allows users to choose one option.
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function SelectExample() {
|
|
const [fruit, setFruit] = useState('apple');
|
|
|
|
return (
|
|
<div>
|
|
<label htmlFor="favorite-fruit">Favorite fruit</label>
|
|
<select
|
|
id="favorite-fruit"
|
|
value={fruit}
|
|
onChange={(event) => setFruit(event.target.value)}>
|
|
<option value="apple">Apple</option>
|
|
<option value="banana">Banana</option>
|
|
<option value="orange">Orange</option>
|
|
</select>
|
|
<p>Selected fruit: {fruit}</p>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
- The `value` attribute is bound to the state (`fruit`)
|
|
- The `onChange` updates the state when the user selects an option
|
|
- This ensures the dropdown value is controlled by React
|
|
|
|
### Summary
|
|
|
|
| Input type | Key element | Key value attribute | State update |
|
|
| --- | --- | --- | --- |
|
|
| Text input | `<input>` | `value` | `setState(event.target.value)` |
|
|
| Checkbox input | `<input>` | `checked` | `setState(event.target.checked)` |
|
|
| Radio group | `<input>` | `checked` | `setState(event.target.value)` |
|
|
| Textarea | `<textarea>` | `value` | `setState(event.target.value)` |
|
|
| Select dropdown | `<select>` | `value` | `setState(event.target.value)` |
|
|
|
|
## Handling complex form state
|
|
|
|
When working with complex forms in React, managing state efficiently is crucial to ensure good performance and maintainability. Forms become complex when they include multiple input fields, dynamic field additions, nested structures, or advanced validation. Below are some strategies to handle complex form state effectively.
|
|
|
|
### Using `useReducer` for complex forms
|
|
|
|
For forms with multiple fields and state dependencies, `useReducer` provides a structured way to manage state updates.
|
|
|
|
```jsx
|
|
import { useReducer } from 'react';
|
|
|
|
// Define reducer function
|
|
function formReducer(state, action) {
|
|
switch (action.type) {
|
|
case 'UPDATE_FIELD':
|
|
return { ...state, [action.field]: action.value };
|
|
case 'RESET':
|
|
return initialState;
|
|
default:
|
|
return state;
|
|
}
|
|
}
|
|
|
|
// Initial form state
|
|
const initialState = {
|
|
name: '',
|
|
email: '',
|
|
age: '',
|
|
};
|
|
|
|
function ComplexFormWithReducer() {
|
|
const [state, dispatch] = useReducer(formReducer, initialState);
|
|
|
|
function handleChange(event) {
|
|
dispatch({
|
|
type: 'UPDATE_FIELD',
|
|
field: event.target.name,
|
|
value: event.target.value,
|
|
});
|
|
}
|
|
|
|
function handleReset() {
|
|
return dispatch({ type: 'RESET' });
|
|
}
|
|
|
|
return (
|
|
<form>
|
|
<input
|
|
type="text"
|
|
name="name"
|
|
value={state.name}
|
|
onChange={handleChange}
|
|
placeholder="Name"
|
|
/>
|
|
<input
|
|
type="email"
|
|
name="email"
|
|
value={state.email}
|
|
onChange={handleChange}
|
|
placeholder="Email"
|
|
/>
|
|
<input
|
|
type="number"
|
|
name="age"
|
|
value={state.age}
|
|
onChange={handleChange}
|
|
placeholder="Age"
|
|
/>
|
|
<button type="button" onClick={handleReset}>
|
|
Reset
|
|
</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
- Keeps state updates predictable and centralized
|
|
- Useful for forms with conditional logic and dependencies
|
|
- Prevents unnecessary re-renders compared to `useState`
|
|
|
|
### Handling dynamic fields
|
|
|
|
When a form allows users to **add or remove fields dynamically** (e.g., multiple email inputs), state should be an array
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function DynamicForm() {
|
|
const [fields, setFields] = useState([{ id: 1, value: '' }]);
|
|
|
|
function addField() {
|
|
setFields([...fields, { id: fields.length + 1, value: '' }]);
|
|
}
|
|
|
|
function removeField(id) {
|
|
setFields(fields.filter((field) => field.id !== id));
|
|
}
|
|
|
|
function handleChange(id, event) {
|
|
const newFields = fields.map((field) =>
|
|
field.id === id ? { ...field, value: event.target.value } : field,
|
|
);
|
|
setFields(newFields);
|
|
}
|
|
|
|
return (
|
|
<form>
|
|
{fields.map((field) => (
|
|
<div key={field.id}>
|
|
<input
|
|
type="text"
|
|
value={field.value}
|
|
onChange={(event) => handleChange(field.id, event)}
|
|
placeholder="Enter value"
|
|
/>
|
|
<button type="button" onClick={() => removeField(field.id)}>
|
|
Remove
|
|
</button>
|
|
</div>
|
|
))}
|
|
<button type="button" onClick={addField}>
|
|
Add Field
|
|
</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
- Useful for forms where users **can add multiple entries** (e.g., multiple email addresses)
|
|
- Keeps UI flexible without unnecessary predefined fields
|
|
|
|
### Using form libraries for complex forms
|
|
|
|
Instead of managing everything manually, libraries like [React Hook Form](https://react-hook-form.com/) and [Formik](https://formik.org/) (unmaintained) simplify complex form state handling.
|
|
|
|
```jsx
|
|
import { useForm, useFieldArray } from 'react-hook-form';
|
|
|
|
function FormWithReactHookForm() {
|
|
const { register, handleSubmit, control } = useForm({
|
|
defaultValues: { emails: [{ value: '' }] },
|
|
});
|
|
|
|
const { fields, append, remove } = useFieldArray({ control, name: 'emails' });
|
|
|
|
function onSubmit(data) {
|
|
console.log(data);
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit(onSubmit)}>
|
|
{fields.map((field, index) => (
|
|
<div key={field.id}>
|
|
<input {...register(`emails.${index}.value`)} placeholder="Email" />
|
|
<button type="button" onClick={() => remove(index)}>
|
|
Remove
|
|
</button>
|
|
</div>
|
|
))}
|
|
<button type="button" onClick={() => append({ value: '' })}>
|
|
Add Email
|
|
</button>
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
- **Reduces re-renders** by using refs instead of state
|
|
- **Easy validation** with built-in support for validation libraries like Zod, Yup, and Joi
|
|
- **Handles dynamic fields efficiently** with `useFieldArray`
|
|
|
|
### Handling nested form structures
|
|
|
|
Sometimes, forms have nested objects (e.g., address with street, city, and zip code).
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function NestedForm() {
|
|
const [form, setForm] = useState({
|
|
name: '',
|
|
address: { street: '', city: '', zip: '' },
|
|
});
|
|
|
|
function handleChange(event) {
|
|
const { name, value } = event.target;
|
|
setForm((prev) => ({
|
|
...prev,
|
|
address: { ...prev.address, [name]: value },
|
|
}));
|
|
}
|
|
|
|
return (
|
|
<form>
|
|
<input
|
|
type="text"
|
|
name="name"
|
|
placeholder="Name"
|
|
value={form.name}
|
|
onChange={(event) => setForm({ ...form, name: event.target.value })}
|
|
/>
|
|
<input
|
|
type="text"
|
|
name="street"
|
|
placeholder="Street"
|
|
value={form.address.street}
|
|
onChange={handleChange}
|
|
/>
|
|
<input
|
|
type="text"
|
|
name="city"
|
|
placeholder="City"
|
|
value={form.address.city}
|
|
onChange={handleChange}
|
|
/>
|
|
<input
|
|
type="text"
|
|
name="zip"
|
|
placeholder="ZIP Code"
|
|
value={form.address.zip}
|
|
onChange={handleChange}
|
|
/>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
- Helps manage related data **cleanly** in a single object
|
|
- Useful for **address, profile, or nested form sections**
|
|
|
|
### Best practices for complex form state
|
|
|
|
- Use `useReducer` for structured state updates
|
|
- Use dynamic fields for flexible input handling
|
|
- Leverage form libraries like React Hook Form or Formik to simplify validation and state management
|
|
- Use nested objects when dealing with grouped form data
|
|
|
|
## Error handling and validation strategies
|
|
|
|
Handling errors and validating user input is essential for creating a smooth user experience in React forms. Validation ensures that users enter the correct data before submission, preventing invalid entries and reducing backend errors. Below are various strategies to handle validation and errors efficiently.
|
|
|
|
### Basic client-side validation with `useState`
|
|
|
|
For simple forms, you can use **state-based validation** to check user input before submission.
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function BasicValidationForm() {
|
|
const [email, setEmail] = useState('');
|
|
const [error, setError] = useState('');
|
|
|
|
function handleSubmit(event) {
|
|
event.preventDefault();
|
|
|
|
if (!email) {
|
|
setError('Email is required');
|
|
return;
|
|
}
|
|
|
|
setError(''); // Clear error if validation passes
|
|
alert(`Submitted: ${email}`);
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit}>
|
|
<label>
|
|
Email:
|
|
<input
|
|
type="email"
|
|
value={email}
|
|
onChange={(event) => setEmail(event.target.value)}
|
|
/>
|
|
</label>
|
|
{error && <p style={{ color: 'red' }}>{error}</p>}
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
- Uses local state to track errors
|
|
- Displays error messages when validation fails
|
|
- Simple but sufficient for basic validation needs
|
|
|
|
### Validate input fields with regular expressions
|
|
|
|
For **structured input fields** like emails, phone numbers, or passwords, **regex-based validation** is useful.
|
|
|
|
```jsx
|
|
function EmailValidationForm() {
|
|
const [email, setEmail] = useState('');
|
|
const [error, setError] = useState('');
|
|
|
|
const validateEmail = (email) => {
|
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
};
|
|
|
|
function handleSubmit(event) {
|
|
event.preventDefault();
|
|
|
|
if (!validateEmail(email)) {
|
|
setError('Please enter a valid email address');
|
|
return;
|
|
}
|
|
|
|
setError('');
|
|
alert(`Valid Email: ${email}`);
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit}>
|
|
<label>
|
|
Email:
|
|
<input
|
|
type="email"
|
|
value={email}
|
|
onChange={(event) => setEmail(event.target.value)}
|
|
/>
|
|
</label>
|
|
{error && <p style={{ color: 'red' }}>{error}</p>}
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
- Uses regex to check if an email follows a valid format
|
|
- Provides real-time feedback to users
|
|
|
|
### Browser built-in HTML5 validation
|
|
|
|
Modern browsers provide built-in validation for certain input types.
|
|
|
|
```jsx
|
|
function HTML5ValidationForm() {
|
|
return (
|
|
<form>
|
|
<label>
|
|
Email:
|
|
<input type="email" required />
|
|
</label>
|
|
<br />
|
|
<label>
|
|
Password (Min 6 characters):
|
|
<input type="password" minLength="6" required />
|
|
</label>
|
|
<br />
|
|
<label>
|
|
Phone (Numbers only):
|
|
<input type="tel" pattern="[0-9]{10}" required />
|
|
</label>
|
|
<br />
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
- `required`: Ensures the field is filled
|
|
- `minLength`: Restricts input length
|
|
- `pattern`: Uses regex directly in HTML
|
|
- Works **without JavaScript**, improving performance
|
|
|
|
### React Hook Form for efficient validation
|
|
|
|
For complex forms, \*_React Hook Form_ provides optimized validation with minimal re-renders.
|
|
|
|
```jsx
|
|
import { useForm } from 'React Hook Form;
|
|
|
|
function HookFormValidation() {
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
formState: { errors },
|
|
} = useForm();
|
|
|
|
const onSubmit = (data) => alert(JSON.stringify(data));
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit(onSubmit)}>
|
|
<label>
|
|
Email:
|
|
<input
|
|
type="email"
|
|
{...register('email', { required: 'Email is required' })}
|
|
/>
|
|
</label>
|
|
{errors.email && <p style={{ color: 'red' }}>{errors.email.message}</p>}
|
|
<label>
|
|
Password:
|
|
<input
|
|
type="password"
|
|
{...register('password', {
|
|
required: 'Password is required',
|
|
minLength: {
|
|
value: 6,
|
|
message: 'Password must be at least 6 characters',
|
|
},
|
|
})}
|
|
/>
|
|
</label>
|
|
{errors.password && (
|
|
<p style={{ color: 'red' }}>{errors.password.message}</p>
|
|
)}
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
- **Uses refs instead of state**, minimizing re-renders
|
|
- **Better performance** for large forms
|
|
- **Built-in error handling** with `formState.errors`
|
|
|
|
### Handle server-side validation
|
|
|
|
Even with client-side validation, backend validation is necessary. You need to know how to display error messages from API responses.
|
|
|
|
```jsx
|
|
import { useState } from 'react';
|
|
|
|
function ServerErrorHandlingForm() {
|
|
const [email, setEmail] = useState('');
|
|
const [error, setError] = useState('');
|
|
|
|
async function handleSubmit(event) {
|
|
event.preventDefault();
|
|
|
|
try {
|
|
setError(null);
|
|
|
|
const response = await fetch('/api/validate-email', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ email }),
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
|
|
const data = await response.json();
|
|
if (!response.ok) {
|
|
throw new Error(data.message || 'Something went wrong');
|
|
}
|
|
|
|
alert('Submission successful!');
|
|
} catch (err) {
|
|
setError(err.message);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit}>
|
|
<label>
|
|
Email:
|
|
<input
|
|
type="email"
|
|
value={email}
|
|
onChange={(event) => setEmail(event.target.value)}
|
|
/>
|
|
</label>
|
|
{error && <p style={{ color: 'red' }}>{error}</p>}
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
- Uses `fetch()` to validate email on the server side
|
|
- Displays API error messages if validation fails
|
|
- Prevents security risks (e.g., hackers bypassing client-side validation)
|
|
|
|
## Best practices for interviews
|
|
|
|
- For simple forms, both controlled and uncontrolled inputs are viable
|
|
- Wrap in `<form>` and leverage the browser form submit events
|
|
- Use HTML5 validation where possible to reduce re-renders for better performance
|
|
- Display clear error messages that guide users on how to fix their inputs
|
|
- Merely client-side validation is insufficient. Server-side validation is also required to prevent security loopholes
|
|
- If 3rd party libraries are allowed (e.g. in take home assignments), React Hook Form is a great addition that provides many useful features helps you achieve better performance at the same time
|
|
|
|
## What you need to know for interviews
|
|
|
|
- **Uncontrolled and controlled forms**: What's the difference, how to use either, and when to use
|
|
- **Form controls**: Be able to build forms that use the various input types
|
|
- **Complex forms**: How to build complex forms and the best practices
|
|
- **Error handling and validation**: How to validate forms and show errors using native browser approaches and React approaches
|
|
|
|
## Practice questions
|
|
|
|
**Coding**:
|
|
|
|
- [Generate Table](/questions/user-interface/generate-table/react?framework=react&tab=coding)
|
|
- [Contact Form](/questions/user-interface/contact-form/react?framework=react&tab=coding)
|
|
- [Mortgage Calculator](/questions/user-interface/mortgage-calculator/react?framework=react&tab=coding)
|
|
- [Temperature Converter](/questions/user-interface/temperature-converter/react?framework=react&tab=coding)
|
|
- [Flight Booker](/questions/user-interface/flight-booker/react?framework=react&tab=coding)
|
|
- [Auth Code Input](/questions/user-interface/auth-code-input/react?framework=react&tab=coding)
|