What Are React Hooks? Explain useState and useEffect
Advertisement
🧩 Scenario
Architecture Walkthrough
What Hooks Are
Hooks are functions whose names start with use that let functional components access React features: state, lifecycle events, context, refs, and memoization. Before hooks (React 16.7 and earlier), these features were only available in class components. Hooks made all of them available in functional components without changing the component model.
Two rules govern all hooks. First, hooks must be called at the top level of a component function, not inside conditions, loops, or nested functions. Second, hooks must be called from functional components or other hooks, not from regular JavaScript functions. These rules exist because React identifies each hook call by its order of invocation in the component. If hooks are called conditionally, the order changes between renders and React's state tracking breaks.
useState: State in Function Components
useState accepts an initial value and returns a pair: the current state value and a setter function. Calling the setter with a new value schedules a re-render. On the next render, the state variable holds the new value. The setter can accept a value directly or an updater function that receives the previous state.
Each useState call manages one piece of state. Multiple pieces of state are managed by multiple useState calls. React tracks them by call order, which is why the rules-of-hooks exist.
useEffect: Side Effects in Function Components
useEffect accepts a callback that runs after render. The optional second argument (the dependency array) controls when the callback re-runs. With no array, it runs after every render. With an empty array, it runs only after the first render. With a non-empty array, it runs after the first render and after any render where one of the listed values changed. The callback can return a cleanup function that runs before the next run of the effect or when the component unmounts.
useEffect replaces componentDidMount (empty array), componentDidUpdate (non-empty array), and componentWillUnmount (the cleanup return).
Key Code Explained
// useState: local state with re-render trigger
function Counter() {
const [count, setCount] = useState(0); // initial value: 0
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>Increment</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
// Multiple state variables
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
await login(email, password);
setIsSubmitting(false);
};
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={(e) => setEmail(e.target.value)} type="email" />
<input value={password} onChange={(e) => setPassword(e.target.value)} type="password" />
<button disabled={isSubmitting}>{isSubmitting ? 'Signing in...' : 'Sign In'}</button>
</form>
);
}
// useEffect: the three dependency array modes
function DataLoader({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null);
// Mount only: empty array
useEffect(() => {
setupAnalytics();
return () => teardownAnalytics();
}, []); // componentDidMount + componentWillUnmount
// Specific dep: runs on mount and when userId changes
useEffect(() => {
let cancelled = false;
fetchUser(userId).then((data) => {
if (!cancelled) setUser(data);
});
return () => { cancelled = true; }; // componentWillUnmount / before next run
}, [userId]); // componentDidUpdate for userId + initial mount
if (!user) return <Skeleton />;
return <UserCard user={user} />;
}
// Custom hook: extract and reuse stateful logic
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize, { passive: true });
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}
// Usage in any component
function ResponsiveLayout() {
const width = useWindowWidth(); // encapsulated state + effect
return <div>{width > 768 ? <DesktopLayout /> : <MobileLayout />}</div>;
}
The custom hook useWindowWidth demonstrates the main advantage of hooks for code reuse. The same logic (adding a resize listener, tracking window width, removing the listener) can be shared between any number of components by calling the hook. Before hooks, this required Higher-Order Components or render props, both of which require restructuring the component tree.
Tradeoffs
| Class component | Hook equivalent | Notes |
|---|---|---|
this.state + setState | useState | Multiple calls for multiple state slices |
componentDidMount | useEffect(() => {}, []) | Empty dep array |
componentDidUpdate | useEffect(() => {}, [dep]) | Specific dep array |
componentWillUnmount | useEffect return | Cleanup function returned from effect |
| HOC / render props | Custom hook | Logic reuse without wrapping the tree |
What Interviewers Actually Check
- Whether you can state the two rules of hooks and explain why they exist
- Whether you can explain the useState trigger-re-render cycle
- Whether you can describe all three useEffect modes (no array, empty array, specific deps)
- Whether you can write a cleanup function and explain when it fires
- Whether you can describe custom hooks and why they are the hook-era replacement for HOCs
Follow-Up Questions
- What are the other built-in hooks besides
useStateanduseEffect, and when would you reach foruseReducer,useContext,useRef, oruseMemo? - How does React know which state belongs to which
useStatecall if they have no identifiers? - What is the
useOptimistichook introduced in React 19 and how does it change the mutation flow? - How would you write a
useDebouncecustom hook that delays a value update? - What is the purpose of
React.StrictModeand how does it affect hook behavior in development?
Common Candidate Mistakes
- Calling
useStateinside anifstatement and not knowing why it breaks React's state tracking - Not knowing that the state setter triggers a re-render and that the new value is only available on the next render
- Describing
useEffectas only running "once on mount" when that is only the empty-array mode - Not knowing that a custom hook is just a function that calls other hooks, not a special React construct
- Claiming hooks are available in class components
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you state the two rules of hooks and explain why they exist?
- Can you write a
useStateexample showing the setter, re-render, and accessing the new value? - Can you write a
useEffectin each of the three modes: every render, mount only, specific dep? - Can you write a custom hook and explain how it enables logic reuse?
- Can you map
componentDidMount,componentDidUpdate, andcomponentWillUnmounttouseEffect?
Summary
Hooks are functions prefixed with use that give functional components access to React features that previously required class components. The two rules that govern them (top-level only, functional components only) exist because React tracks hook state by call order; conditional or nested calls would change the order across renders and corrupt React's state tracking.
useState manages a single piece of local state. Calling the setter schedules a re-render; the new value is available in the next render. useEffect runs side effects after renders. Its behavior depends on the dependency array: no array runs after every render, empty array runs after the first render only, a non-empty array runs after mount and whenever any listed value changes. The optional cleanup function returned from the effect fires before the next run and on unmount.
Custom hooks are the key code-reuse pattern of the hooks era. A custom hook is a regular function that calls other hooks. It extracts stateful logic from a component, making it reusable across multiple components without wrapping the component tree in Higher-Order Components or passing render props.
Can I use hooks inside class components?
No. Hooks are only valid inside functional components and other hooks. Class components use this.state and lifecycle methods instead.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement