Skip to main content

forms-1-control

Controlled vs. Uncontrolled Components: Explain the difference between controlled components, where React manages form state, and uncontrolled components, where the DOM manages form state. Discuss when to use each and how TypeScript helps with controlled components through type checking.

Controlled Components

In controlled components, form data is handled by the React component's state. You can use TypeScript to enforce the types of data handled by the form.

Example:

import React, { useState } from 'react';

type FormData = {
username: string;
age: number;
};

const ControlledForm: React.FC = () => {
const [formData, setFormData] = useState<FormData>({ username: '', age: 0 });

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type } = e.target;
setFormData(prevState => ({
...prevState,
[name]: type === 'number' ? parseInt(value) : value,
}));
};

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
// Submit logic
};

return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
/>
<input
type="number"
name="age"
value={formData.age}
onChange={handleChange}
/>
<button type="submit">Submit</button>
</form>
);
};

In this example, we're using a TypeScript type to define the shape of our form data. This ensures that the formData state and the handleChange method are type-safe.

Uncontrolled Components

Uncontrolled components use refs to get form values directly from the DOM instead of managing the state in React.

Example:

import React, { useRef } from 'react';

const UncontrolledForm: React.FC = () => {
const usernameRef = useRef<HTMLInputElement>(null);
const ageRef = useRef<HTMLInputElement>(null);

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const data = {
username: usernameRef.current?.value,
age: ageRef.current?.valueAsNumber,
};
// Submit logic
};

return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
ref={usernameRef}
/>
<input
type="number"
name="age"
ref={ageRef}
/>
<button type="submit">Submit</button>
</form>
);
};

Here, we don't manage the state of our form inputs in React. Instead, we use the useRef hook to directly interact with the input elements. TypeScript is not as critical in this pattern since we are not dealing with state management, but it still provides types for refs, improving developer experience and code reliability.

When to Use Each

You would use controlled components when you need to implement complex features like real-time validation, conditional input fields, or dynamic form inputs. TypeScript enhances controlled components by ensuring that all operations on the form state are type-safe, reducing the likelihood of bugs.

Uncontrolled components are preferred when you want to integrate with non-React code, when you need to manage focus, selection, or animations, or when performance is a critical concern, as they avoid the overhead of React's state updates.

TypeScript's role with uncontrolled components is more about typing the refs and ensuring that they are used correctly within your components. It provides less value than with controlled components, where the entire form state is managed within React, but it is still beneficial for maintaining consistency and preventing type-related bugs.