React Hooks for MERN Developers
Understanding useState, useEffect, and Custom Hooks
Target Audience: MERN Developers Prerequisites: React Components, Props, State Goal: Understand how React remembers information, performs side effects, and reuses logic using Hooks.
How Does React Remember Information Between Renders?
Imagine a simple React component.
function Counter() {
let count = 0;
return (
<>
<h1>{count}</h1>
<button onClick={() => count++}>
Increment
</button>
</>
);
}
Looks correct.
Click the button.
Nothing happens.
Why?
Because every time React renders the component, the function executes again.
Render 1
count = 0
↓
Click Button
↓
count = 1
↓
React Renders Again
↓
count = 0
The variable is recreated.
React doesn't remember ordinary JavaScript variables.
This raises an important question.
How does React remember information between renders?
The answer is Hooks.
Why Were React Hooks Introduced?
Before Hooks, React developers used Class Components.
class Counter extends React.Component {
state = {
count: 0
};
}
Large applications became difficult to manage because:
Logic was spread across lifecycle methods.
Reusing stateful logic was difficult.
Components became very large.
thiskeyword caused confusion.Lifecycle methods were hard to understand.
Hooks solved these problems.
Benefits of Hooks
Hooks made React:
Simpler
More readable
Easier to maintain
Easier to test
Better at reusing logic
Less dependent on classes
Today, almost all modern React applications use Function Components + Hooks.
What Are Hooks?
Hooks are special React functions that allow function components to use:
State
Side Effects
Context
Reusable Logic
Examples
useState
useEffect
useContext
useMemo
useCallback
Every Hook begins with use.
Understanding useState
What is useState?
useState allows React components to store information between renders.
const [count, setCount] = useState(0);
Think of it like this:
React
↓
Stores Value
↓
Returns Current State
↓
Returns Function to Update It
Breaking Down useState
const [count, setCount] = useState(0);
Here:
count
Current value.
setCount()
Updates value.
0
Initial value.
Counter Example
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<h1>{count}</h1>
<button
onClick={() => setCount(count + 1)}
>
Increment
</button>
</>
);
}
Flow
Button Click
↓
setCount()
↓
State Changes
↓
React Re-renders
↓
Updated UI
Why Can't We Change State Directly?
Wrong
count = count + 1;
React won't know anything changed.
Correct
setCount(count + 1);
The setter tells React
"State has changed. Please render again."
Multiple State Variables
A component can have many pieces of state.
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [age, setAge] = useState(21);
Each state variable is independent.
State-Driven UI
React follows one simple rule:
State
↓
UI
If state changes,
UI changes automatically.
Example
Theme = Light
↓
Button Click
↓
Theme = Dark
↓
Entire UI Updates
Common State Patterns
Examples of state:
Counter
Theme
Search Input
Form Fields
Shopping Cart
Modal Visibility
Selected Tab
Loading Spinner
Not everything should be state.
Only values that affect rendering belong in state.
Understanding React Re-renders
Many beginners think:
"React refreshes the whole page."
It doesn't.
React re-renders components.
What is a Re-render?
Whenever state changes,
React runs the component function again.
State Changes
↓
Component Function Runs Again
↓
New JSX
↓
Compare with Previous UI
↓
Update DOM
What Triggers a Re-render?
1. State Updates
setCount(count + 1);
2. Props Changes
Parent passes different props.
↓
Child renders again.
3. Context Changes
Context updates.
↓
Consumers re-render.
React Rendering Flow
State
↓
Render
↓
Virtual DOM
↓
Diffing
↓
Real DOM Updates
React updates only the parts of the page that changed.
Common Misconceptions
Wrong
Every render is bad.
Correct
Rendering is normal.
Unnecessary rendering is the problem.
Avoid Unnecessary State
Bad
const [fullName, setFullName] =
useState(first + last);
Better
const fullName =
first + last;
Derived values usually don't need state.
Understanding useEffect
Why Does useEffect Exist?
React components should mainly describe UI.
Sometimes we need to interact with the outside world.
Examples
API Requests
Timers
Browser Events
Local Storage
WebSockets
These are called Side Effects.
What is a Side Effect?
Anything that affects something outside the component.
Examples
Fetching Data
Saving Data
Console Logging
Changing Document Title
Adding Event Listeners
Starting Timers
What Does useEffect Do?
It lets React synchronize your component with external systems.
useEffect(() => {
console.log("Component rendered");
});
Data Fetching Example
useEffect(() => {
fetch("/api/users")
.then(...)
}, []);
Flow
Component Appears
↓
useEffect Runs
↓
API Request
↓
State Updates
↓
UI Updates
Event Listener Example
useEffect(() => {
window.addEventListener(...);
}, []);
Timer Example
useEffect(() => {
const id = setInterval(...);
}, []);
Cleanup Functions
Some side effects must be cleaned up.
Example
useEffect(() => {
const id = setInterval(...);
return () => {
clearInterval(id);
};
}, []);
Cleanup prevents:
Memory leaks
Duplicate timers
Duplicate listeners
Dependency Arrays Explained
One of the most misunderstood React topics.
Syntax
useEffect(() => {
}, []);
The second argument is the dependency array.
Empty Dependency Array
useEffect(() => {
}, []);
Runs
Once
↓
After First Render
Perfect for
API Calls
Initial Setup
Dependencies Included
useEffect(() => {
}, [user]);
Runs
First Render
↓
Whenever user changes
No Dependency Array
useEffect(() => {
});
Runs
Every Render
Usually not what you want.
Common Dependency Mistakes
Missing Dependencies
React warns
Missing Dependency
because your effect may use stale values.
Infinite Loops
useEffect(() => {
setCount(count + 1);
}, [count]);
Flow
Effect
↓
setCount()
↓
Render
↓
Effect
↓
setCount()
↓
Forever
Always understand why an effect runs before updating state inside it.
Common useEffect Patterns
Fetching Data
Render
↓
Fetch API
↓
Update State
↓
Display Data
Browser Events
Mount
↓
Add Event Listener
↓
Unmount
↓
Remove Listener
Synchronizing External Systems
Examples
Local Storage
Cookies
WebSocket Connections
Browser Title
Analytics
useEffect keeps React synchronized with systems outside React.
Lifecycle Thinking
Older React
componentDidMount
componentDidUpdate
componentWillUnmount
Modern React
Think
"Synchronize with external systems."
Not
"Run after mount."
This mindset leads to cleaner effects.
Custom Hooks
What Are Custom Hooks?
Custom Hooks are JavaScript functions that use other Hooks.
Example
function useCounter() {
}
Notice the name starts with
use
Why Do Custom Hooks Exist?
Imagine five components all fetch user data.
Without custom hooks
Component A
↓
Fetch Logic
Component B
↓
Fetch Logic
Component C
↓
Fetch Logic
Lots of duplication.
With a custom hook
useUser()
↓
Component A
Component B
Component C
Shared logic.
Reusing Stateful Logic
Custom Hooks allow multiple components to share logic while maintaining independent state.
Each component gets its own instance.
Separating Concerns
Bad
Dashboard.jsx
↓
500 Lines
↓
Everything Together
Better
Dashboard
↓
useAuth()
↓
useFetch()
↓
useTheme()
↓
Cleaner Component
Common Custom Hooks
Authentication
useAuth()
Data Fetching
useUsers()
useProducts()
usePosts()
Forms
useForm()
Theme
useTheme()
Window Size
useWindowSize()
Device Information
useOnlineStatus()
useDarkMode()
useLocalStorage()
When Should You Create a Custom Hook?
Create one when:
Logic is repeated.
State management is shared.
Side effects are duplicated.
Components become too large.
Don't create custom hooks for one-time logic.
Rules of Hooks
React Hooks follow strict rules.
Rule 1
Only call Hooks at the top level.
Wrong
if (loggedIn) {
useEffect(...);
}
Correct
useEffect(...);
Rule 2
Only call Hooks inside:
React Components
Custom Hooks
Never inside:
Loops
Conditions
Nested Functions
Why These Rules Exist
React identifies Hooks by the order in which they are called.
Example
Render 1
useState
useEffect
useState
Render 2 must follow the same order.
Changing the order confuses React.
Organizing Hooks
A common structure
Component
↓
Hooks
↓
Derived Values
↓
Functions
↓
JSX
Keeps components readable.
Common Hook Mistakes
Forgetting dependencies.
Updating state unnecessarily.
Using useEffect for calculations.
Writing huge useEffect blocks.
Creating custom hooks too early.
Calling Hooks conditionally.
Thinking in Hooks
Instead of asking
"Where do I write this code?"
Ask
"What responsibility does this code have?"
If it changes UI
↓
State
If it synchronizes with the outside world
↓
useEffect
If it is reused
↓
Custom Hook
React Hooks Mental Model
User Action
↓
setState()
↓
React Re-renders
↓
New JSX
↓
DOM Updates
↓
useEffect Synchronizes
Everything revolves around:
State
Rendering
Synchronization
Modern React Architecture
App
├── Components
├── Local State
├── Custom Hooks
├── Context
└── Effects
Hooks help keep each concern separate and reusable.
Interview Questions
Why were Hooks introduced?
To simplify component development, replace many class component patterns, and enable reusable stateful logic in function components.
What does useState do?
It stores state between renders and provides a setter function that tells React to re-render when the state changes.
What triggers a re-render?
State updates
Prop changes
Context updates
What is useEffect?
A Hook used to synchronize React components with external systems such as APIs, timers, browser events, and storage.
What is a Side Effect?
Any operation that interacts with something outside React's rendering process.
Examples include API calls, timers, subscriptions, and browser APIs.
What does an empty dependency array mean?
The effect runs once after the component's initial render.
What happens without a dependency array?
The effect runs after every render.
Why do infinite loops happen in useEffect?
Because the effect updates state, which triggers another render, causing the effect to run again repeatedly.
What are Custom Hooks?
Reusable JavaScript functions that combine Hooks and encapsulate stateful logic for use across multiple components.
Why must Hooks always be called at the top level?
Because React relies on the order of Hook calls remaining the same on every render.
Key Takeaways
Hooks allow function components to manage state, side effects, and reusable logic.
useStategives components memory across renders and drives UI updates.React re-renders components when state, props, or context change.
useEffectis designed for synchronizing components with external systems—not simply replacing lifecycle methods.Dependency arrays control when effects run and help avoid unnecessary work or infinite loops.
Cleanup functions prevent memory leaks by removing subscriptions, timers, and event listeners.
Custom Hooks encourage code reuse and separation of concerns by extracting shared stateful logic.
Following the Rules of Hooks ensures predictable execution and prevents subtle bugs.
Thinking in Hooks means separating state, rendering, and synchronization into clear responsibilities.
Mastering these fundamentals prepares you for advanced React concepts such as Context, reducers, routing, data fetching libraries, and state management solutions.