How do you reset a component state in React?
The short answer
The cleanest way to reset a component's state is to give it a different key prop. When the key changes, React unmounts the old instance and mounts a new one with fresh state. For partial resets, you can manually set each piece of state back to its initial value. Both approaches have their place — the key technique is best for full resets, and manual resets are better when you want fine-grained control.
The key prop technique
This is the approach most React developers reach for, and it's the one the React docs recommend. Here's the idea: React uses the key prop to track a component's identity. If the key changes, React treats it as an entirely new component — it destroys the old one and creates a new one from scratch, including all of its state.
function App() {
const [selectedUserId, setSelectedUserId] = useState(1);
return (
<>
<UserSelector onSelect={setSelectedUserId} />
<ProfileForm
key={selectedUserId}
userId={selectedUserId}
/>
</>
);
}
function ProfileForm({ userId }) {
const [name, setName] = useState('');
const [bio, setBio] = useState('');
const [avatar, setAvatar] = useState(null);
return (
<form>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<textarea
value={bio}
onChange={(e) => setBio(e.target.value)}
placeholder="Bio"
/>
<button type="submit">Save</button>
</form>
);
}When the user switches from editing user 1 to user 2, selectedUserId changes, which changes the key on ProfileForm. React unmounts the old form and mounts a fresh one. All the state — name, bio, avatar — starts from its initial values. No stale data leaking between users.
Why the key technique works
React uses keys to decide whether a component at a given position in the tree is "the same" across renders. You've probably used keys in lists:
{
items.map((item) => (
<ListItem key={item.id} data={item} />
));
}The same mechanism works on any component. When React sees that the key changed, it says: "This is a different component now." It runs cleanup (unmount the old instance) and initialization (mount the new instance) — exactly as if the old component was removed from the tree and a new one was added in its place.
This means all useState hooks get their initial values, all useEffect cleanup functions run, and all useEffect setups run fresh. It's a complete reset.
Manual reset
Sometimes you don't want to unmount the component. Maybe it has focus state, animation state, or expensive setup that you want to preserve. In that case, you reset individual pieces of state by hand.
A clean way to do this is to define your initial values once and reuse them:
const INITIAL_STATE = {
name: '',
email: '',
message: '',
};
function ContactForm() {
const [formData, setFormData] = useState(INITIAL_STATE);
const handleReset = () => {
setFormData(INITIAL_STATE);
};
const handleChange = (e) => {
setFormData((prev) => ({
...prev,
[e.target.name]: e.target.value,
}));
};
return (
<form>
<input
name="name"
value={formData.name}
onChange={handleChange}
/>
<input
name="email"
value={formData.email}
onChange={handleChange}
/>
<textarea
name="message"
value={formData.message}
onChange={handleChange}
/>
<button type="button" onClick={handleReset}>
Clear form
</button>
</form>
);
}This is straightforward, but it only works well when your state is consolidated. If you have state spread across many useState calls, you'd need to reset each one individually, which gets tedious.
The useReducer pattern
When a component has complex state with multiple fields, useReducer gives you a natural way to handle resets:
const initialState = {
name: '',
email: '',
quantity: 1,
agreed: false,
};
function reducer(state, action) {
switch (action.type) {
case 'update':
return { ...state, [action.field]: action.value };
case 'reset':
return initialState;
default:
return state;
}
}
function OrderForm() {
const [state, dispatch] = useReducer(
reducer,
initialState
);
return (
<form>
<input
value={state.name}
onChange={(e) =>
dispatch({
type: 'update',
field: 'name',
value: e.target.value,
})
}
/>
<button
type="button"
onClick={() => dispatch({ type: 'reset' })}
>
Start over
</button>
</form>
);
}A single dispatch({ type: 'reset' }) returns the entire state to its starting point. This scales well as your state grows — adding a new field doesn't require updating the reset logic.
The common mistake: resetting with useEffect
A pattern you'll sometimes see — and should avoid — is trying to reset state in response to a prop change using useEffect:
function ProfileForm({ userId }) {
const [name, setName] = useState('');
const [bio, setBio] = useState('');
// Avoid this pattern
useEffect(() => {
setName('');
setBio('');
}, [userId]);
// ...
}This technically works, but it causes an extra render. On the first render after userId changes, the component renders with the stale state. Then the effect fires, updates the state, and triggers a second render with the reset values. The user might briefly see the old data flash on screen.
The key approach avoids this entirely because the component is brand new — there's no stale state to clean up.
When to use which
Use the key technique when:
- You want a complete, clean reset of all state
- The component doesn't have expensive setup you need to preserve
- You're switching between distinct "instances" (editing user A vs user B, switching tabs)
Use manual reset when:
- You want to reset some state but keep other state intact
- The component has focus, scroll position, or animations you want to preserve
- You're building a "clear form" button that doesn't change the underlying entity
Use useReducer when:
- The component has many pieces of related state
- You want a single, centralized reset action
- The state transitions are complex enough to benefit from a reducer
Why interviewers ask this
This question tests whether you understand React's reconciliation model — specifically how keys control component identity. The key technique is elegant and correct, but many developers don't know about it and instead reach for the useEffect approach (which has the extra render problem). Knowing the right approach shows you understand how React decides to reuse or recreate components, which is fundamental to building UIs that behave predictably.