React Hooks

State, lifecycle, and reusable logic in functional components

useState useEffect Rules of Hooks Custom Hooks

Table of Contents

1. Introduction: What are React Hooks?

React Hooks are functions that let you "hook into" React state and lifecycle features from functional components.

  • Introduced in: React 16.8 (February 2019)
  • What they do: Allow functional components to have state, lifecycle methods, context, refs, and more
  • Why they matter: Before Hooks, these features were only available in class components

Analogy: Hooks are like power adapters. Your functional component (like a laptop) can now plug into React's internal features (state and lifecycle) that were previously only available to class components.

JSX// BEFORE HOOKS (Class Component - 15+ lines) class Counter extends React.Component { state = { count: 0 }; increment = () => this.setState({ count: this.state.count + 1 }); render() { return <button onClick={this.increment}>{this.state.count}</button>; } } // AFTER HOOKS (Functional Component - 5 lines!) function Counter() { const [count, setCount] = useState(0); return <button onClick={() => setCount(count + 1)}>{count}</button>; }

2. The Problem Hooks Solve (Why Hooks Were Created)

Before Hooks, React had three major problems that made code difficult to write, understand, and reuse.

2.1 Problem 1: Wrapper Hell (HOCs and Render Props)

To reuse stateful logic between components, developers had to use Higher-Order Components (HOCs) or Render Props, which created deeply nested "wrapper hell."

JSX// BEFORE HOOKS: Wrapper Hell export default withRouter( withAuth( withTheme( withData(MyComponent) ) ) ); // Result in JSX — "Pyramid of Doom" <DataProvider> <ThemeProvider> <AuthProvider> <RouterProvider> <MyComponent /> </RouterProvider> </AuthProvider> </ThemeProvider> </DataProvider>

The problem: Hard to debug, hard to type (TypeScript becomes complex), performance overhead from multiple wrapper layers.

JSX// AFTER HOOKS: Clean and flat function MyComponent() { const router = useRouter(); const user = useAuth(); const theme = useTheme(); const data = useData(); return <div>{data}</div>; }

2.2 Problem 2: Giant Components with Complex Logic

Lifecycle methods forced related logic to be split across multiple methods, while unrelated logic was grouped together.

JSX// BEFORE HOOKS: Scattered Logic class UserDashboard extends React.Component { componentDidMount() { this.fetchUser(); this.setupSubscription(); this.startPolling(); this.setupEventListeners(); } componentDidUpdate(prevProps) { if (prevProps.userId !== this.props.userId) { this.fetchUser(); this.setupSubscription(); } } componentWillUnmount() { this.cleanupSubscription(); this.stopPolling(); this.removeEventListeners(); } }

The Hook solution: Group related logic together with multiple useEffect calls — setup and cleanup side by side.

JSX// AFTER HOOKS: Grouped Logic function UserDashboard({ userId }) { const [user, setUser] = useState(null); useEffect(() => { fetchUser(userId).then(setUser); }, [userId]); useEffect(() => { const subscription = setupSubscription(userId); return () => subscription.cleanup(); }, [userId]); useEffect(() => { const interval = setInterval(() => pollData(), 5000); return () => clearInterval(interval); }, []); return <div>{user?.name}</div>; }

2.3 Problem 3: Confusing Classes (this binding, lifecycle methods)

JSX// PROBLEMS WITH CLASS COMPONENTS // 1. Confusing 'this' binding class Button extends React.Component { handleClick() { console.log(this); // 'this' is undefined without binding! } render() { return <button onClick={this.handleClick.bind(this)}>Click</button>; } } // 2. Constructor boilerplate + binding every handler // 3. Forgetting super(props) // 4. Complex lifecycle names to remember
JSX// AFTER HOOKS: Simple functions! function Button() { const [count, setCount] = useState(0); const handleClick = () => setCount(count + 1); return <button onClick={handleClick}>{count}</button>; }

3. What Were React Components Like Before Hooks?

Before Hooks (React 16.7 and earlier), there were two ways to create components:

Option 1: Functional Components (Stateless)

JSX// Could only accept props and return JSX // Could NOT have: State, Lifecycle methods, Refs, Context function Greeting({ name }) { return <h1>Hello, {name}!</h1>; }

Option 2: Class Components (Stateful)

JSXclass Greeting extends React.Component { state = { count: 0 }; componentDidMount() { console.log('Component mounted'); } render() { return ( <div> <h1>Hello, {this.props.name}!</h1> <p>Count: {this.state.count}</p> </div> ); } }

The gap: You had to choose between simplicity (functional components with no features) or power (class components with complexity). Hooks bridge this gap — functional components can now do everything class components can do, with simpler syntax.

4. Types of React Hooks (Overview)

4.1 Basic Hooks (Most Commonly Used)

HookPurposeBasic Syntax
useStateAdd state to functional componentsconst [state, setState] = useState(initial)
useEffectSide effects (API calls, DOM updates, subscriptions)useEffect(() => { /* effect */ }, [deps])
useContextAccess React context without nestingconst value = useContext(MyContext)

4.2 Additional Hooks (Less Common but Powerful)

HookPurpose
useReducerComplex state logic (like Redux but built-in)
useCallbackMemoize functions to prevent unnecessary re-renders
useMemoMemoize expensive calculations
useRefMutable references (DOM elements, persisted values)
useImperativeHandleCustomize ref exposure to parent components
useLayoutEffectSynchronous version of useEffect (DOM measurements)
useDebugValueCustom hook labels in React DevTools
useIdGenerate unique IDs for accessibility attributes
useTransitionMark updates as non-urgent for better UX
useDeferredValueDefer updating a value to keep UI responsive

4.3 Custom Hooks

You can create your own hooks to reuse stateful logic across components:

JSXfunction useWindowWidth() { const [width, setWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = () => setWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return width; } function MyComponent() { const width = useWindowWidth(); return <p>Window width is {width}px</p>; }

5. Rules of Hooks (The Two Golden Rules)

React Hooks have two rules that are absolutely mandatory. Breaking them will cause bugs that are hard to debug.

5.1 Rule 1: Only Call Hooks at the Top Level

Don't call Hooks inside loops, conditions, or nested functions. Always call them at the top level of your React function.

JSX// WRONG — Called inside condition function BadComponent({ shouldUseHook }) { if (shouldUseHook) { const [count, setCount] = useState(0); // Conditional hook call! } } // WRONG — Called inside loop or nested function for (let i = 0; i < items.length; i++) { const [state, setState] = useState(null); } // CORRECT — Called at top level function GoodComponent() { const [count, setCount] = useState(0); if (count > 5) return <div>Count is high!</div>; return <button onClick={() => setCount(count + 1)}>{count}</button>; }

Why this rule matters: React relies on the order of Hook calls to know which state belongs to which Hook. Conditional calls change the order and React gets confused.

5.2 Rule 2: Only Call Hooks from React Functions

Only call Hooks from React functional components or custom hooks (functions that start with use).

JSX// WRONG — regular JavaScript function function regularFunction() { const [data, setData] = useState(null); } // CORRECT — React component function MyComponent() { const [count, setCount] = useState(0); return <button onClick={() => setCount(count + 1)}>{count}</button>; } // CORRECT — custom hook function useCustomHook() { const [value, setValue] = useState(null); return value; }

5.3 ESLint Plugin for Rules of Hooks

Bash / JSON# Install the plugin npm install eslint-plugin-react-hooks --save-dev // .eslintrc.json { "plugins": ["react-hooks"], "rules": { "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn" } }

Benefits: catches violations at compile time, suggests fixes for missing dependencies, automates rule enforcement.

6. Benefits of Using Hooks

6.1 Simpler and Cleaner Code

JSX// CLASS COMPONENT (~20 lines) class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; this.increment = this.increment.bind(this); } increment() { this.setState({ count: this.state.count + 1 }); } render() { return ( <div> <p>Count: {this.state.count}</p> <button onClick={this.increment}>Increment</button> </div> ); } } // FUNCTIONAL WITH HOOKS (~8 lines) function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }
MetricClass ComponentFunctional with Hooks
Lines of code~20~8
Conceptsthis, binding, constructor, render()Just functions
BoilerplateHighMinimal
ReadabilityModerateExcellent

6.2 Reusability of Stateful Logic

JSXfunction useMousePosition() { const [position, setPosition] = useState({ x: 0, y: 0 }); useEffect(() => { const handleMouseMove = (e) => setPosition({ x: e.clientX, y: e.clientY }); window.addEventListener('mousemove', handleMouseMove); return () => window.removeEventListener('mousemove', handleMouseMove); }, []); return position; } function ComponentA() { const { x, y } = useMousePosition(); return <div>Mouse is at ({x}, {y})</div>; } function ComponentB() { const { x, y } = useMousePosition(); return <div>Cursor: {x},{y}</div>; }

6.3 Easier to Understand and Maintain

JSXfunction UserProfile({ userId }) { const [user, setUser] = useState(null); useEffect(() => { fetchUser(userId).then(setUser); const subscription = subscribeToUser(userId); return () => subscription.unsubscribe(); }, [userId]); const [posts, setPosts] = useState([]); useEffect(() => { fetchPosts(userId).then(setPosts); }, [userId]); return <div>{user?.name} has {posts.length} posts</div>; }

6.4 Better Performance

JSX// useMemo — memoize expensive calculations const processedData = useMemo(() => { return data.map(item => expensiveOperation(item)); }, [data]); // useCallback — memoize functions to prevent child re-renders const handleClick = useCallback(() => { console.log('Clicked!'); }, []);

6.5 No More this Binding Confusion

JSX// CLASS: 'this' confusion — bind, arrow wrapper, or broken handler // HOOKS: Just regular functions function Button() { const handleClick = () => console.log('Clicked!'); return <button onClick={handleClick}>Click</button>; }

6.6 Gradual Adoption (No Breaking Changes)

JSX// Mix class and functional components in the same app class LegacyComponent extends React.Component { render() { return <NewHookComponent />; } } function NewHookComponent() { const [data, setData] = useState(null); return <div>{data}</div>; }

No need to rewrite existing class components — start using hooks in new components and migrate at your own pace.

7. Complete Working Examples

7.1 Example 1: Class Component vs Functional Component with Hooks

JSX// Class version class Timer extends React.Component { state = { seconds: 0 }; componentDidMount() { this.interval = setInterval(() => { this.setState(s => ({ seconds: s.seconds + 1 })); }, 1000); } componentWillUnmount() { clearInterval(this.interval); } render() { return <p>Elapsed: {this.state.seconds}s</p>; } } // Hooks version — same behavior, less code function Timer() { const [seconds, setSeconds] = useState(0); useEffect(() => { const interval = setInterval(() => setSeconds(s => s + 1), 1000); return () => clearInterval(interval); }, []); return <p>Elapsed: {seconds}s</p>; }

7.2 Example 2: Breaking Complex Logic into Multiple Hooks

JSXfunction Dashboard({ userId }) { const user = useUser(userId); // custom hook: fetch + subscribe const notifications = useNotifications(userId); const theme = useTheme(); if (!user) return <LoadingSpinner />; return ( <div className={theme}> <h1>Welcome, {user.name}</h1> <NotificationList items={notifications} /> </div> ); } function useUser(userId) { const [user, setUser] = useState(null); useEffect(() => { fetchUser(userId).then(setUser); const sub = subscribeToUser(userId); return () => sub.unsubscribe(); }, [userId]); return user; }

7.3 Example 3: Creating a Custom Hook

JSXfunction useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { setLoading(true); fetch(url) .then(res => res.json()) .then(setData) .catch(setError) .finally(() => setLoading(false)); }, [url]); return { data, loading, error }; } function UserList() { const { data, loading, error } = useFetch('/api/users'); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error.message}</p>; return ( <ul> {data.map(user => <li key={user.id}>{user.name}</li>)} </ul> ); }

8. Common Mistakes and How to Avoid Them

Mistake 1: Conditional Hook Calls

JSX// WRONG if (shouldFetch) { const [data, setData] = useState(null); } // CORRECT — always call hook; condition inside effect const [data, setData] = useState(null); useEffect(() => { if (shouldFetch) fetchData().then(setData); }, [shouldFetch]);

Mistake 2: Missing Dependencies in useEffect

JSX// WRONG — userId missing from deps useEffect(() => { fetchUser(userId).then(setUser); }, []); // CORRECT useEffect(() => { fetchUser(userId).then(setUser); }, [userId]);

Mistake 3: Not Using Cleanup Functions

JSX// WRONG — interval continues after unmount useEffect(() => { const interval = setInterval(() => console.log('Tick'), 1000); }, []); // CORRECT useEffect(() => { const interval = setInterval(() => console.log('Tick'), 1000); return () => clearInterval(interval); }, []);

Mistake 4: Calling Hooks in Event Handlers

JSX// WRONG const handleClick = () => { const [count, setCount] = useState(0); }; // CORRECT const [count, setCount] = useState(0); const handleClick = () => setCount(count + 1);

Mistake 5: Infinite Loops in useEffect

JSX// WRONG — no deps array causes infinite loop useEffect(() => { setCount(count + 1); }); // CORRECT — run once, or use functional update useEffect(() => { setCount(c => c + 1); }, []);

9. Summary Cheat Sheet

JSX// THE TWO RULES OF HOOKS // 1. Only call Hooks at the TOP LEVEL // 2. Only call Hooks from REACT FUNCTIONS (components or custom hooks) // BASIC HOOKS SYNTAX const [state, setState] = useState(initialValue); useEffect(() => { // Side effect code return () => { /* Cleanup (optional) */ }; }, [dependencies]); const value = useContext(MyContext); // CUSTOM HOOK PATTERN function useCustomHook(parameter) { const [data, setData] = useState(null); useEffect(() => { // Effect logic return () => { /* Cleanup */ }; }, [parameter]); return data; } // BENEFITS SUMMARY // 1. Simpler code (no classes, no 'this') // 2. Reusable logic (custom hooks) // 3. Grouped concerns (related code together) // 4. Better performance (useMemo, useCallback) // 5. Gradual adoption (works with existing code) // ESLINT PLUGIN // npm install eslint-plugin-react-hooks // "react-hooks/rules-of-hooks": "error" // "react-hooks/exhaustive-deps": "warn" // COMMON USE CASES // useState → Local component state // useEffect → API calls, subscriptions, DOM updates // useContext → Access global state (theme, auth) // useReducer → Complex state logic // useCallback → Memoized functions // useMemo → Memoized values // useRef → DOM references, persisted values // Custom Hooks → Reusable logic across components

Next Steps

  • useState Deep Dive – Advanced patterns, lazy initialization, functional updates
  • useEffect Deep Dive – Dependencies, cleanup, data fetching patterns
  • useContext and Context API – Global state management
  • useReducer – Complex state logic (like Redux)
  • useRef and DOM Manipulation – Accessing DOM elements directly
  • Custom Hooks – Building your own reusable hooks

React Hooks MCQ Practice

10 Basic MCQs 10 Advanced MCQs

10 Basic React Hooks MCQs

1

Hooks let functional components:

AUse state and lifecycle features
BReplace HTML
CRun only on server
DSkip JSX
Explanation: Hooks bridge functions with React features.
2

Rules of Hooks: call hooks only:

AAt top level of React function
BInside if blocks freely
CIn regular JS functions
DIn loops conditionally
Explanation: Never call hooks inside conditions/loops.
3

useState is used for:

ALocal component state
BRouting only
CFetching CSS
DDOM queries only
Explanation: Stores mutable values that trigger re-render.
4

useEffect is for:

ASide effects after render
BDeclaring components
CReplacing JSX
Dnpm install
Explanation: API calls, subscriptions, DOM sync.
5

useContext reads:

AValue from nearest Provider
Bnpm cache
CCSS variables
DRouter only
Explanation: Avoid prop drilling for shared data.
6

useRef persists value:

AAcross renders without re-render
BOnly one render
CIn localStorage
DIn SQL
Explanation: Mutating ref.current does not re-render.
7

Custom hooks must start with:

Ause prefix
Bhook prefix optional
Cget prefix
Don prefix
Explanation: Naming convention enables lint rules.
8

Only call hooks from:

AReact function components or custom hooks
BEvent handlers directly
CsetTimeout callbacks freely
DClass methods
Explanation: Hooks rely on call order per component.
9

useMemo caches:

AExpensive computed values
BDOM nodes only
CEntire apps
Dpackage.json
Explanation: Recompute when dependencies change.
10

useCallback caches:

AFunction references
BJSX elements only
CCSS files
DRoutes
Explanation: Stable function for child memo deps.

10 Advanced React Hooks MCQs

1

eslint-plugin-react-hooks enforces:

ARules of Hooks and exhaustive deps
BCSS order
Cnpm versions
DHTML validity
Explanation: Catch missing effect dependencies.
2

Calling hooks conditionally breaks because:

AReact tracks hooks by call order
BHooks are CSS
CBrowser forbids it
DJSX invalid
Explanation: Order must be identical every render.
3

useLayoutEffect runs:

AAfter DOM mutations, before paint
BBefore render
COnly on server
DNever in browser
Explanation: Use for layout measurements; prefer useEffect by default.
4

useReducer fits when:

AComplex state transitions
BNo state needed
COnly styling
DOnly refs
Explanation: dispatch({type:'increment'}) pattern.
5

useImperativeHandle with forwardRef:

AExpose limited methods on ref to parent
BReplace all props
CDelete children
DGlobal state
Explanation: Encapsulate DOM API on child.
6

useId in React 18 provides:

AStable unique IDs for accessibility
BRandom keys for lists
CRouter ids
Dnpm package names
Explanation: Helps associate label/input across SSR.
7

Strict Mode double-invoking effects helps:

ASurface side-effect cleanup bugs in dev
BRun production twice
CDisable hooks
DRemove keys
Explanation: Dev-only behavior to test effect cleanup.
8

Custom hook can compose:

AMultiple built-in hooks
BOnly one useState ever
CNo other hooks
DOnly class APIs
Explanation: Share stateful logic between components.
9

useDebugValue is for:

ALabeling custom hooks in DevTools
BProduction logging only
CReplacing console
DCSS debug
Explanation: Optional dev-only description.
10

Hooks replaced class features like:

AcomponentDidMount via useEffect
Brender via useRender hook
Cconstructor via useConstructor
DshouldComponentUpdate via useShould
Explanation: Effects consolidate lifecycle logic.

15 React Hooks Interview Questions & Answers

Easy Medium Hard
1What are React Hooks?easy
Answer: Functions that let functional components use state, effects, context, and more.
2State the Rules of Hooks.easy
Answer: Only call at top level; only from React functions or custom hooks.
3Why not call useState inside if?medium
Answer: Conditional calls change hook order and corrupt React internal state.
4Difference useState vs useRef?medium
Answer: useState triggers re-render; useRef holds mutable value without re-render.
5What is a custom hook?medium
Answer: Reusable function using hooks, named with use, sharing stateful logic.
6When use useEffect vs useLayoutEffect?hard
Answer: useEffect for most side effects; useLayoutEffect when you must read/mutate layout before paint.
7What is useReducer?medium
Answer: Hook like Redux pattern for complex state with reducer function and dispatch.
8Can hooks replace Redux?hard
Answer: For local/simple global state yes; large apps may still use Redux/Zustand.
9What does exhaustive-deps lint rule do?medium
Answer: Warns when effect dependencies are missing, preventing stale closures.
10useMemo vs useCallback?medium
Answer: useMemo memoizes values; useCallback memoizes functions.
11Are hooks backported to class components?easy
Answer: No—classes use lifecycle methods; hooks are for functions.
12useContext performance note?hard
Answer: All consumers re-render when context value changes—split contexts or memoize.
13What is useImperativeHandle?hard
Answer: Customize instance value exposed to parent refs when using forwardRef.
14Hooks and testing?medium
Answer: Render with React Testing Library; wrap providers; test behavior not implementation.
15Future of class components?easy
Answer: Still supported but new code should use function components and hooks.