useState and useEffect
Implement or sketch code for useState and useEffect. Explain the logic, complexity, and pros/cons of this approach.
Answers use simple, clear English.
Quick interview answer
Logic: useState triggers re-render on update — functional updater avoids stale closures. useEffect runs after paint; cleanup on unmount/deps change.
Detailed answer
Logic: useState triggers re-render on update — functional updater avoids stale closures. useEffect runs after paint; cleanup on unmount/deps change. Deps array controls re-run. Complexity notes included in code section when present. Pros: Core hooks model side effects declaratively. Cons: Missing deps cause stale bugs; effect for derived state often wrong — compute inline. Core: useState triggers re-render on update — functional updater avoids stale closures. useEffect runs after paint; cleanup on unmount/deps change. Deps array controls re-run. Real-time example: Fetch user profile on mount: useEffect with [userId] deps; abort fetch in cleanup. Pros: Core hooks model side effects declaratively. Cons: Missing deps cause stale bugs; effect for derived state often wrong — compute inline. Common mistakes: Empty deps with props/state inside; no cleanup on subscriptions/timers. Best practices: Exhaustive deps or eslint-plugin-react-hooks; prefer useQuery/RSC for data fetch. Audience level: Fresher.
Full explanation
useState triggers re-render on update — functional updater avoids stale closures. useEffect runs after paint; cleanup on unmount/deps change. Deps array controls re-run.
Real example & use case
Fetch user profile on mount: useEffect with [userId] deps; abort fetch in cleanup.
Pros & cons
Pros: Core hooks model side effects declaratively. Cons: Missing deps cause stale bugs; effect for derived state often wrong — compute inline.
Code example
'use client';
import { useEffect, useState } from 'react';
export function Profile({ userId }: { userId: string }) {
const [name, setName] = useState<string | null>(null);
useEffect(() => {
const ctrl = new AbortController();
fetch(`/api/users/${userId}`, { signal: ctrl.signal })
.then((r) => r.json())
.then((u) => setName(u.name))
.catch(() => {});
return () => ctrl.abort();
}, [userId]);
return <p>{name ?? 'Loading…'}</p>;
}Practice code · typescript (view only · no execution)
'use client';
import { useEffect, useState } from 'react';
export function Profile({ userId }: { userId: string }) {
const [name, setName] = useState<string | null>(null);
useEffect(() => {
const ctrl = new AbortController();
fetch(`/api/users/${userId}`, { signal: ctrl.signal })
.then((r) => r.json())
.then((u) => setName(u.name))
.catch(() => {});
return () => ctrl.abort();
}, [userId]);
return <p>{name ?? 'Loading…'}</p>;
}Common mistakes
Empty deps with props/state inside; no cleanup on subscriptions/timers.
Best practices
Exhaustive deps or eslint-plugin-react-hooks; prefer useQuery/RSC for data fetch.
Follow-up questions
- How would you test useState and useEffect?
- What metrics prove useState and useEffect is healthy in prod?
- How does useState and useEffect change at 10× traffic?