Mastering useEffect in React: A Fun Guide with Counter, Data, and Timer Examples

If you’re learning React, chances are you’ve heard about useEffect and wondered:
"Why does this hook exist? Why not just write everything in functions?"
Well, let’s break it down with an easy and fun example. We’ll build a little app that:
Keeps track of a count
Keeps track of some data
Runs a timer that can start and stop
And along the way, we’ll see how useEffect works in 3 different modes.
import React, { useEffect, useState } from "react";
const App = () => {
// React states to hold different values
const [count, setCount] = useState(0); // keeps track of count
const [data, setData] = useState(10); // keeps track of data
const [start, setStart] = useState(false); // flag to check if timer started or stopped
const [seconds, setSeconds] = useState(0); // timer counter
// A simple function that logs a message
const getExecuted = () => {
console.log("hi i will get executed always");
};
// Function to increase count
const Increment = () => {
setCount((prev) => prev + 1);
};
// Function to update data
const updateData = () => {
setData((prev) => prev + 10);
};
// useEffect without dependency array → runs EVERY time component re-renders
useEffect(() => {
getExecuted();
}); // run on every render
// useEffect with empty array → runs ONLY once when component mounts
useEffect(() => {
console.log("hi i will get executed once");
}, []); // run only once
// useEffect with [data] dependency → runs when 'data' value changes
useEffect(() => {
console.log("hi i am will get executed when data will change", data);
}, [data]); // runs whenever data updates
// Timer logic
useEffect(() => {
let interval;
if (start) {
console.log("Timer started");
interval = setInterval(() => {
// increment seconds every 1 second
setSeconds((prev) => prev + 1);
}, 1000);
} else {
console.log("Timer stopped");
}
// cleanup function → clears the timer when component unmounts or 'start' changes
return () => {
if (interval) clearInterval(interval);
console.log("Timer cleaned up");
};
}, [start]); // runs whenever start (true/false) changes
return (
<div>
<h1>Count: {count}</h1>
<h2>Data: {data}</h2>
{/* Buttons to update count and data */}
<button onClick={Increment}>Increment Count</button>
<button onClick={updateData}>Update Data</button>
{/* Timer Display */}
<h2>Timer: {seconds} sec</h2>
{/* Timer controls */}
<button onClick={() => setStart(true)}>▶ Start</button>
<button onClick={() => setStart(false)}>⏹ Stop</button>
</div>
);
};
export default App;
⚙️ The Code
(You saw the full code above 👆. Don’t worry, we’ll explain it step by step.)
1. 📊 States: Our App’s Memory
» We use useState to keep track of different things:
const [count, setCount] = useState(0);
const [data, setData] = useState(10);
const [start, setStart] = useState(false);
const [seconds, setSeconds] = useState(0);
» Think of states like notebooks in which React writes stuff down.
countis like the number of cookies eaten.datais some random number we increase by 10.startis a switch for the timer (on/off).secondsis literally the timer counting.
2. 🔁 useEffect #1 – Runs on Every Render
useEffect(() => {
getExecuted();
});
This guy runs every single time the component re-renders.
Imagine someone shouting "I’m alive!" every time your app blinks. That’s this effect.
3. 🎉 useEffect #2 – Runs Only Once
useEffect(() => {
console.log("hi i am will get executed when data will change", data);
}, [data]);
This one is smarter — it runs only when data changes.
So if you eat 10 more cookies 🍪, React says: "Oh! Data changed, let me run this again.
5. ⏱ Timer with Cleanup
The most exciting part — a timer:
useEffect(() => {
let interval;
if (start) {
console.log("Timer started");
interval = setInterval(() => {
setSeconds((prev) => prev + 1);
}, 1000);
} else {
console.log("Timer stopped");
}
return () => {
if (interval) clearInterval(interval);
console.log("Timer cleaned up");
};
}, [start]);
When you hit ▶ Start, it begins counting seconds.
When you hit ⏹ Stop, the timer halts.
The cleanup function makes sure we don’t create a new timer on top of an old one (otherwise, chaos: multiple timers racing 🏎️).
🎯 Final Thoughts
This little app is like a playground for learning useEffect. You saw:
How it runs on every render
How it runs only once
How it runs only when a dependency changes
How to use it with timers and cleanup
So the next time someone asks:
👉 “When should I use useEffect?”
You can say:
When you want something to happen every time → no dependencies
When you want something to happen only once → empty array
[]When you want something to happen when X changes →
[X]
Now it’s your turn 🎮:
👉 Try adding a Reset Timer button. Or maybe a Pause/Resume button. Once you do that, you’ll be officially leveling up in React! .
💡 If you enjoyed this post, share it with your developer friends or drop a comment with what you want me to explain next. And don’t forget to bookmark this page for quick React refreshers.



