React.js + Next.js · Module 3. Forms and Effects · Lesson 8 · Point 8
Side Effects and the React useEffect Hook
React can display information from props and state. Sometimes a component must also communicate with something outside React.
For Pizza Palace, the menu component may need to ask the Pizza Palace API which pizzas are currently available.
That request is a side effect. React gives us the useEffect() Hook for work that must happen because a component appeared or because one of its dependencies changed.
We will build the idea in this order:
Render the component
↓
Show the interface
↓
Run the Effect
↓
Receive external data
↓
Update state and render again
This lesson uses regular functions and simple Pizza Palace examples.
REACT · 01
1. Understand Mounting, Updating, and Unmounting
A React component has three important lifecycle moments.
| Moment | Simple meaning | Pizza Palace example |
|---|---|---|
| Mounting | The component appears on the page | The pizza menu opens |
| Updating | The component renders again | The selected category changes |
| Unmounting | The component is removed | The pizza menu closes |
Here is a small example:
import { useState } from "react";
function PizzaMenu() {
return <h2>Pizza menu is open</h2>;
}
export default function App() {
const [showMenu, setShowMenu] = useState(true);
function handleClick() {
setShowMenu(!showMenu);
}
return (
<main>
<button onClick={handleClick}>
Show / hide menu
</button>
{showMenu && <PizzaMenu />}
</main>
);
}
Browser Result
PizzaMenu mounts. Hiding it unmounts it.When showMenu changes from true to false, React removes PizzaMenu from the page. When it becomes true again, React mounts a new instance.
REACT · 02
2. A State Update Can Cause a Re-render
State lets a component remember information.
const [pizzaCount, setPizzaCount] = useState(0);
pizzaCountis the current value;setPizzaCountupdates the value;0is the initial value.
Use the state setter inside an event handler:
import { useState } from "react";
export default function CartCounter() {
const [pizzaCount, setPizzaCount] = useState(0);
console.log("CartCounter rendered");
function handleAddPizza() {
setPizzaCount(pizzaCount + 1);
}
return (
<section>
<h2>Pizzas in cart: {pizzaCount}</h2>
<button onClick={handleAddPizza}>
Add one pizza
</button>
</section>
);
}
Browser Result
The sequence is:
Customer clicks
↓
handleAddPizza runs
↓
setPizzaCount updates state
↓
React renders CartCounter again
A re-render is not a new mount. The component remains on the page, and useState remembers the current value.
React also does not replace the entire DOM. It updates only the parts that need to change.
REACT · 03
3. Keep Rendering Code Pure
Rendering code runs when React calls the component function. It should calculate and return the interface.
This calculation is safe during rendering:
const pizzaPrice = 12;
const quantity = 2;
const total = pizzaPrice * quantity;
This is incorrect:
import { useState } from "react";
export default function BrokenCounter() {
const [count, setCount] = useState(0);
// Wrong: this updates state during rendering.
setCount(count + 1);
return <h1>Count: {count}</h1>;
}
Why does it fail?
Render
↓
Update state
↓
Render again
↓
Update state again
↓
Too many re-renders
Put an update caused by a click inside an event handler. Put synchronization with an external system inside an Effect.
REACT · 04
4. Choose Between Rendering, an Event, and an Effect
These three places have different jobs.
| Place | When it runs | Pizza Palace example |
|---|---|---|
| Rendering code | When React renders | Calculate the cart total |
| Event handler | After a specific user action | Submit an order after a click |
| Effect | Because the component appeared or a dependency changed | Load the current pizza menu |
Normal Rendering Calculation
const total = price * quantity;
No server or browser API is involved, so we do not need useEffect().
Event Handler
function handleOrder() {
console.log("Customer placed the order");
}
The customer clicking a button directly causes this work. It belongs in an event handler.
Effect
useEffect(function loadMenu() {
console.log("Ask the Pizza Palace API for the menu");
}, []);
No button click is required. The menu request happens because the menu component appeared.
The most important rule is:
If there is no external system, you probably do not need an Effect.
REACT · 05
5. Do Not Send an HTTP Request During Rendering
This code is incorrect:
import { useState } from "react";
import axios from "axios";
interface Pizza {
id: number;
name: string;
}
export default function PizzaMenu() {
const [pizzas, setPizzas] = useState<Pizza[]>([]);
// Wrong: this request runs during every render.
axios
.get<Pizza[]>("/api/pizzas")
.then(function savePizzas(response) {
setPizzas(response.data);
});
return <p>Found pizzas: {pizzas.length}</p>;
}
The problem is the location of the request, not Axios.
PizzaMenu renders
↓
Axios sends a request
↓
The response updates state
↓
PizzaMenu renders again
↓
Axios sends another request
This may create repeated requests, unnecessary server traffic, and unpredictable behavior.
The request must be moved out of the rendering calculation.
REACT · 06
6. Write Your First useEffect
First import useEffect:
import { useEffect } from "react";
Then call it at the top level of the component:
useEffect(function showMessage() {
console.log("The Pizza Palace menu appeared");
}, []);
There are two important parts:
function showMessage() { ... } → Effect setup function
[] → Dependency array
Complete example:
import { useEffect } from "react";
export default function PizzaMenu() {
console.log("1. PizzaMenu rendered");
useEffect(function showMessage() {
console.log("2. Effect ran");
}, []);
return <h1>Pizza Palace menu</h1>;
}
Browser Result
Call useEffect() before return. Do not call it inside a condition, loop, event handler, or .map() callback.
REACT · 07
7. Understand the Execution Order
Look at the letters in this example:
import { useEffect } from "react";
export default function App() {
console.log("A");
useEffect(function runEffect() {
console.log("B");
}, []);
console.log("C");
return <h1>Pizza Palace</h1>;
}
The usual order is:
A
C
B
Step by step:
- React calls
App(). - JavaScript prints
A. - React registers the Effect but does not run its setup yet.
- JavaScript prints
C. - React commits the interface.
- React runs the Effect and prints
B.
Think of useEffect() as telling React:
After this interface is committed, synchronize it with the external system.
REACT · 08
8. Understand the Dependency Array
The dependency array tells React when the Effect must synchronize again.
No Dependency Array
useEffect(function runAfterEveryCommit() {
console.log("Effect ran");
});
This runs after every committed render.
Empty Dependency Array
useEffect(function runAfterMounting() {
console.log("Effect ran");
}, []);
This runs after the component mounts. Ordinary re-renders do not run it again.
Dependency Inside the Array
useEffect(function categoryChanged() {
console.log("Category:", category);
}, [category]);
This runs after mounting and again when category changes.
| Syntax | When the Effect runs |
|---|---|
useEffect(setup) |
After every committed render |
useEffect(setup, []) |
After mounting |
useEffect(setup, [category]) |
After mounting and when category changes |
You cannot freely hide values from this array. Every reactive value used by the Effect must be included as a dependency.
REACT · 11
11. Understand Strict Mode and Future Cleanup
During development, React Strict Mode may run one extra setup-and-cleanup cycle for an Effect. React does this to reveal missing cleanup logic.
You may therefore notice two development requests even with:
useEffect(function loadPizzaMenu() {
// Request code
}, []);
Remember:
[]means the Effect belongs to each mount;- unmounting and mounting again runs it again;
- Strict Mode performs an additional development check;
- production behavior does not include that extra development-only check.
Some Effects need cleanup:
connect → disconnect
start timer → stop timer
add listener → remove listener
start request → cancel it or ignore an outdated result
Cleanup and request cancellation will be studied in a later point. For now, do not remove Strict Mode just to hide repeated development behavior.
REACT · 12
12. Summary and Practice
Main Rules
Rendering calculates the interface.
Events respond to specific user actions.
Effects synchronize with external systems.
| Concept | Meaning |
|---|---|
| Mount | A component appears |
| Re-render | React calls the component again |
| Unmount | A component disappears |
| Side effect | Work involving something outside rendering |
useEffect() |
Synchronize after React commits a render |
| Dependency array | Describes when synchronization must happen again |
Do Not Use an Effect For
- multiplying a price by a quantity;
- filtering an array for display;
- responding directly to a button click;
- creating values that can be calculated during rendering.
Use an Effect For
- loading the Pizza Palace menu when a client-side component appears;
- connecting to a live order-status service;
- synchronizing with a browser API;
- starting something that needs later cleanup.
Practice
- Add
console.log()during rendering and inside an Effect. Compare their order. - Build a component that loads
/api/pizzasafter mounting. - Add loading, error, and success interfaces.
- Add a
categorystate and explain why an Effect that uses it needs[category]. - Explain why placing the Axios request directly in the component body is dangerous.
Next lesson: Effect cleanup, request cancellation, and asynchronous Effect patterns.