Introduction to Hooks
Introduction to React Hooks covers what Hooks are, why they replaced class-centric patterns, the use naming convention, built-in Hooks, custom Hooks, Rules of Hooks, and a practical mental model.
React Hooks are functions that let React function components use features such as state, effects, context, refs, and other React capabilities without writing class components.
Hooks became a major part of modern React development because they allow component logic to be expressed directly inside function components while making reusable logic easier to organize.
The most important idea to understand is:
Hooks let function components use React features and share stateful logic in a structured way.
Why Do We Need Hooks?
Before Hooks, state and many other React features were primarily associated with class components.
A class component could manage state like this:
class Counter extends React.Component {state = {count: 0};render() {return (<button onClick={() => this.setState({ count: this.state.count + 1 })}>{this.state.count}</button>);}}
Modern React can express the same idea using a function component and useState:
import { useState } from "react";function Counter() {const [count, setCount] = useState(0);return (<button onClick={() => setCount(count + 1)}>{count}</button>);}
The Hook-based version is generally easier to read and allows related logic to stay together.
What Is a Hook?
A Hook is a special React function whose name starts with use.
Examples include:
useStateuseEffectuseRefuseMemouseCallbackuseReduceruseContext
You call Hooks from React function components or from custom Hooks.
For example:
const [count, setCount] = useState(0);
Here:
useState↓React Hook0↓Initial statecount↓Current statesetCount↓Function used to update state
The `use` Naming Convention
React Hooks use names beginning with:
use
For example:
useState()useEffect()useRef()
Custom Hooks should follow the same convention:
useOnlineStatus()useFetch()useLocalStorage()
The use prefix is important because it communicates that a function follows Hook rules.
Built-in Hooks
React provides several built-in Hooks for different purposes.
useState
Used to add state to a function component.
import { useState } from "react";function Counter() {const [count, setCount] = useState(0);return (<button onClick={() => setCount(count + 1)}>Count: {count}</button>);}
Use useState when a component needs to remember information that can change over time.
useEffect
Used to synchronize a component with an external system.
import { useEffect } from "react";function PageTitle() {useEffect(() => {document.title = "React Course";}, []);return <h1>React Course</h1>;}
Effects are commonly used when React needs to interact with something outside the component's rendering process.
Examples include:
- browser APIs
- subscriptions
- timers
- network-related synchronization
- external systems
useRef
Used to keep a mutable value between renders without causing a re-render when that value changes.
It is also commonly used to access a DOM element.
import { useRef } from "react";function SearchInput() {const inputRef = useRef(null);function focusInput() {inputRef.current.focus();}return (<><input ref={inputRef} /><button onClick={focusInput}>Focus Input</button></>);}
useMemo
Used to cache the result of a calculation between renders when appropriate.
import { useMemo } from "react";function ProductList({ products }) {const expensiveValue = useMemo(() => {return products.filter(product => product.active);}, [products]);return <div>{expensiveValue.length} active products</div>;}
useMemo should be used when memoization provides a meaningful performance benefit. It should not be added automatically to every calculation.
useCallback
Used to cache a function definition between renders when appropriate.
import { useCallback } from "react";function Search({ onSearch }) {const handleSearch = useCallback(value => {onSearch(value);},[onSearch]);return <button onClick={() => handleSearch("React")}>Search</button>;}
Like useMemo, useCallback is primarily a performance optimization and should be used when there is a specific reason for it.
useReducer
Used for managing state through a reducer function.
import { useReducer } from "react";function reducer(state, action) {switch (action.type) {case "increment":return { count: state.count + 1 };default:return state;}}function Counter() {const [state, dispatch] = useReducer(reducer, {count: 0});return (<buttononClick={() => dispatch({ type: "increment" })}>Count: {state.count}</button>);}
useReducer can be useful when state transitions are more complex than simple independent updates.
Hooks and Function Components
Hooks are designed to work with function components.
A typical function component might look like:
function Profile() {return <h1>Profile</h1>;}
With Hooks:
function Profile() {const [name, setName] = useState("");return (<inputvalue={name}onChange={event => setName(event.target.value)}/>);}
The component remains a function, but it can now use React-managed state.
Hooks Do Not Make Functions Stateful in the JavaScript Sense
It is important to understand that Hooks do not turn a normal JavaScript function into a class-like object.
React manages the Hook state and associates it with the component's rendering process.
When you write:
const [count, setCount] = useState(0);
React keeps track of that state across renders.
Conceptually:
Initial render↓useState(0)↓count = 0User interaction↓setCount(1)↓React schedules an update↓Component renders again↓count = 1
Rendering and Hooks
A React component can render many times.
For example:
function Counter() {const [count, setCount] = useState(0);console.log("Rendering");return (<button onClick={() => setCount(count + 1)}>{count}</button>);}
Every state update can cause the component to render again.
The Hook call:
useState(0)
does not mean that 0 replaces the state on every render.
The initial value is used when the state is initialized. React then preserves the current state across subsequent renders.
Hooks and State Updates
Consider:
const [count, setCount] = useState(0);
When the user clicks:
setCount(count + 1);
React schedules an update.
The component renders again with the new state.
count = 0↓setCount(1)↓React schedules update↓render↓count = 1
This is one of the most important mental models for understanding Hooks.
Multiple Hooks in One Component
A component can use multiple Hooks.
function UserProfile() {const [name, setName] = useState("");const [age, setAge] = useState(0);useEffect(() => {document.title = name || "Profile";}, [name]);return (<div><inputvalue={name}onChange={event => setName(event.target.value)}/><inputtype="number"value={age}onChange={event => setAge(Number(event.target.value))}/></div>);}
Here the component uses:
useState↓nameuseState↓ageuseEffect↓document title synchronization
Each Hook has a different responsibility.
Hooks and Component Logic
Hooks allow related logic to stay close together.
For example:
function Dashboard() {const [user, setUser] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => {// Load user information}, []);return (<div>{loading? "Loading...": user?.name}</div>);}
State and effect logic can be read directly alongside the UI that uses it.
This is one of the major benefits of Hooks.
Custom Hooks
Hooks can also be composed into reusable custom Hooks.
For example:
function useCounter(initialValue = 0) {const [count, setCount] = useState(initialValue);function increment() {setCount(value => value + 1);}function decrement() {setCount(value => value - 1);}return {count,increment,decrement};}
Then:
function Counter() {const {count,increment,decrement} = useCounter(10);return (<div><p>{count}</p><button onClick={decrement}>-</button><button onClick={increment}>+</button></div>);}
The custom Hook packages reusable stateful logic.
Sharing Logic vs Sharing State
This distinction is extremely important.
Suppose two components use:
const [count, setCount] = useState(0);
They do not automatically share the same count.
Each component instance gets its own state.
Counter A↓count = 5Counter B↓count = 10
A custom Hook shares logic, not automatically the same state instance.
To share state between components, React provides other patterns such as:
- lifting state up
- Context
- external state-management solutions
Rules of Hooks
Hooks have strict usage rules.
The two fundamental rules are:
Rule 1: Only Call Hooks at the Top Level
Do not call Hooks inside:
- loops
- conditions
- nested functions
- event handlers
Incorrect:
if (isLoggedIn) {const [user, setUser] = useState(null);}
Correct:
const [user, setUser] = useState(null);if (!isLoggedIn) {return <Login />;}
Rule 2: Only Call Hooks from React Functions
Hooks should be called from:
- React function components
- custom Hooks
For example:
function Profile() {const [name, setName] = useState("");// ...}
A custom Hook:
function useUser() {const [user, setUser] = useState(null);// ...}
Do not call a Hook from an ordinary utility function that is not a component or custom Hook.
Why Hook Rules Exist
React relies on the consistent ordering of Hook calls across renders.
Consider:
function Component() {const [name, setName] = useState("");const [age, setAge] = useState(0);// ...}
Conceptually:
Hook #1 → nameHook #2 → age
If Hook calls could appear conditionally:
if (condition) {useState("");}useState(0);
the order could change between renders.
That would make it ambiguous which state belongs to which Hook call.
Therefore, Hook calls must remain in a stable order.
Hooks and Conditional Logic
You can use conditions around the result of a Hook.
Incorrect:
if (isAdmin) {const [name, setName] = useState("");}
Correct:
const [name, setName] = useState("");if (!isAdmin) {return <AccessDenied />;}
The Hook is called unconditionally, while the component's rendering logic can still be conditional.
Hooks and Event Handlers
Do not call Hooks inside event handlers.
Incorrect:
function Button() {function handleClick() {const [count, setCount] = useState(0);}return (<button onClick={handleClick}>Click</button>);}
Instead, call the Hook at the component level:
function Button() {const [count, setCount] = useState(0);function handleClick() {setCount(value => value + 1);}return (<button onClick={handleClick}>{count}</button>);}
Hooks and JavaScript Functions
A custom Hook is still a JavaScript function.
For example:
function useCounter() {const [count, setCount] = useState(0);return {count,setCount};}
The important distinction is that the function uses React Hooks, so it follows the Rules of Hooks.
The use prefix makes its purpose clear.
Hooks and Reusability
Without custom Hooks, similar logic may be duplicated:
function ComponentA() {// online status logic}function ComponentB() {// same online status logic}
With a custom Hook:
function useOnlineStatus() {// reusable logic}
Components can use:
const isOnline = useOnlineStatus();
This gives us:
Reusable behavior↓Custom Hook↓Multiple components
Choosing the Right Hook
A simple decision process can help.
Need component state?↓useState
Need complex state transitions?↓useReducer
Need to synchronize with an external system?↓useEffect
Need a DOM reference or persistent mutable value?↓useRef
Need to cache an expensive calculation?↓useMemo
Need to cache a function reference?↓useCallback
Need reusable stateful logic?↓Custom Hook
These are guidelines rather than rigid rules. The simplest solution should generally be preferred.
Hooks Are Not a Replacement for Everything
Not every piece of logic requires a Hook.
For example, this is just normal JavaScript:
function formatPrice(price) {return `$${price.toFixed(2)}`;}
There is no reason to create:
function useFormatPrice(price) {// unnecessary}
Custom Hooks are useful when the logic actually involves React features or needs to coordinate with React's rendering lifecycle.
Hooks and Derived Data
Not every calculated value should become state.
For example:
function Cart({ items }) {const total = items.reduce((sum, item) => sum + item.price,0);return <p>Total: ${total}</p>;}
There is no need for:
const [total, setTotal] = useState(0);
when total can be calculated directly from existing data.
A useful principle is:
Don't store data in state when it can be derived reliably from existing props or state.
Hooks and Side Effects
A side effect is work that interacts with something outside React's rendering calculation.
Examples include:
Browser APIsTimersSubscriptionsExternal systemsNetwork synchronization
useEffect is designed for synchronizing with external systems.
For example:
useEffect(() => {const connection = createConnection();connection.connect();return () => {connection.disconnect();};}, []);
The cleanup function helps remove the external connection when the effect needs to be stopped or restarted.
Hooks and Performance
Some Hooks can help optimize performance:
useMemouseCallback
But optimization should be based on an actual need.
Don't automatically write:
const value = useMemo(() => 10 + 20, []);
for a trivial calculation.
Similarly, don't wrap every function in:
useCallback(...)
without a reason.
First write clear code.
Then optimize where profiling or a known rendering pattern indicates a benefit.
Hooks and Debugging
When debugging Hooks, useful questions include:
Is the component rendering again?Is state updating?Are dependencies correct?Is an effect running when expected?Is a Hook being called conditionally?Is a custom Hook returning the expected value?
React Developer Tools can also help inspect component state and understand component behavior.
Common Mistakes
1. Calling Hooks Conditionally
Incorrect:
if (condition) {useEffect(() => {});}
Keep the Hook call at the top level instead.
2. Calling Hooks Inside Loops
Incorrect:
for (const item of items) {useState(item);}
Hooks should not be created dynamically in loops.
3. Calling Hooks Inside Event Handlers
Incorrect:
function handleClick() {useState(0);}
Call the Hook in the component instead.
4. Creating State for Derived Values
Avoid unnecessary duplicated state.
Instead of:
const [fullName, setFullName] = useState("");const fullName = firstName + " " + lastName;
derive it directly:
const fullName = `${firstName} ${lastName}`;
5. Using useEffect for Everything
Not every calculation or state update belongs in an Effect.
Use Effects when you need to synchronize with an external system.
6. Overusing Memoization
Don't use useMemo and useCallback simply because they exist.
Use them when they solve a real performance or referential-identity problem.
7. Confusing Custom Hooks with Shared State
Calling the same custom Hook from two components generally creates separate Hook state for each component instance.
Best Practices
1. Follow the Rules of Hooks
Keep Hook calls at the top level of React components and custom Hooks.
2. Prefer Simple Hooks
Use the simplest Hook that solves the problem.
3. Keep Components Focused
Don't create one enormous component containing unrelated state and effects.
4. Extract Repeated Stateful Logic
When the same React-specific logic appears in multiple components, consider creating a custom Hook.
5. Avoid Unnecessary State
Derive values from props and existing state whenever practical.
6. Use Effects for External Synchronization
Don't use useEffect merely because something happens after rendering.
7. Optimize Intentionally
Use useMemo and useCallback when there is a clear reason.
8. Name Custom Hooks Clearly
Prefer:
useOnlineStatususeWindowSizeuseLocalStorageuseFetch
over vague names.
Real-World Example: Online Status
A common reusable behavior is detecting whether the browser is online.
A custom Hook could encapsulate that behavior:
import { useEffect, useState } from "react";function useOnlineStatus() {const [isOnline, setIsOnline] = useState(navigator.onLine);useEffect(() => {function handleOnline() {setIsOnline(true);}function handleOffline() {setIsOnline(false);}window.addEventListener("online",handleOnline);window.addEventListener("offline",handleOffline);return () => {window.removeEventListener("online",handleOnline);window.removeEventListener("offline",handleOffline);};}, []);return isOnline;}
A component can now simply use:
function Status() {const isOnline = useOnlineStatus();return (<p>{isOnline? "You are online": "You are offline"}</p>);}
The component doesn't need to know how the browser event listeners work.
This is the power of custom Hooks:
Complex React logic↓Custom Hook↓Simple component API
Hooks Mental Model
Think of Hooks as a bridge between a function component and React's capabilities.
Function Component│┌───────────┼───────────┐▼ ▼ ▼useState useEffect useRef│ │ │▼ ▼ ▼State Effects Refs│ │ │└───────────┼───────────┘▼React
And custom Hooks provide a way to package that logic:
React APIs↓Custom Hook↓Reusable logic↓Multiple components
Practice Exercises
Exercise 1 — Counter with useState
Create a counter with:
- increment
- decrement
- reset
Use useState.
Exercise 2 — Document Title with useEffect
Create a component that updates the browser document title whenever a counter changes.
Exercise 3 — Focus Input with useRef
Create an input and a button.
Clicking the button should focus the input.
Exercise 4 — Derived Data
Create a shopping cart.
Calculate the total from the cart items without storing the total separately in state.
Exercise 5 — Custom Counter Hook
Create:
useCounter()
that returns:
countincrementdecrementreset
Use it in two different components.
Observe that each component has its own state.
Exercise 6 — Online Status Hook
Create:
useOnlineStatus()
and display:
Online
or:
Offline
based on the browser's connection state.
Interview questions & answers
Interview preparation
12 questions grouped by difficulty — expand any card for a model answer and follow-up probe.
Beginner
1What are React Hooks?
BeginnerModel answer
Hooks are React functions that allow function components and custom Hooks to use React features such as state, effects, refs, and other capabilities.
2What is useEffect?
BeginnerModel answer
useEffectis used to synchronize a component with an external system.3Do custom Hooks share state?
BeginnerModel answer
No.
Calling the same custom Hook from different component instances normally gives each instance its own Hook state.
4Should every function use useCallback?
BeginnerModel answer
No.useCallbackshould be used when caching a function reference has a meaningful purpose, such as working with memoized children or Hook dependencies.
Intermediate
5Why were Hooks introduced?
IntermediateModel answer
Hooks provide a way to use React features in function components and make reusable stateful logic easier to organize.
6What is useRef?
IntermediateModel answer
useRefprovides a persistent mutable reference and is commonly used for accessing DOM elements without causing a re-render when the ref value changes.7What are the Rules of Hooks?
IntermediateModel answer
Hooks should be called at the top level of React function components or custom Hooks, not inside conditions, loops, nested functions, or event handlers.
8Should derived values be stored in state?
IntermediateModel answer
Usually not.
If a value can be calculated reliably from existing props or state, it can often be derived directly.
Advanced
9What is useState?
AdvancedModel answer
useStateis a Hook used to add state to a function component.10What is a custom Hook?
AdvancedModel answer
A custom Hook is a reusable JavaScript function whose name starts withuseand that can use other Hooks to encapsulate reusable React-specific logic.11Should every calculation use useMemo?
AdvancedModel answer
No.useMemois a performance optimization and should be used when memoization provides a meaningful benefit.12Can Hooks be used in class components?
AdvancedModel answer
The Hooks API is designed for function components and custom Hooks, not for class components.
Quick Cheat Sheet
| Hook | Primary Purpose |
|---|---|
useState | Component state |
useEffect | External synchronization |
useRef | Persistent mutable reference / DOM reference |
useMemo | Cache a calculated value |
useCallback | Cache a function reference |
useReducer | Manage complex state transitions |
| Custom Hook | Reuse React-specific logic |
Basic state
const [count, setCount] = useState(0);
Effect
useEffect(() => {// synchronize with an external system}, []);
Ref
const inputRef = useRef(null);
Memoized calculation
const result = useMemo(() => calculate(value),[value]);
Memoized callback
const handleClick = useCallback(() => doSomething(value),[value]);
Reducer
const [state, dispatch] = useReducer(reducer,initialState);
Custom Hook
function useSomething() {// Hook logic}
Fundamental rule
Call Hooks↓at the top level↓of React components or custom Hooks