Skip to main content

Command Palette

Search for a command to run...

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

Updated
4 min read
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.

  • count is like the number of cookies eaten.

  • data is some random number we increase by 10.

  • start is a switch for the timer (on/off).

  • seconds is 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:

  1. How it runs on every render

  2. How it runs only once

  3. How it runs only when a dependency changes

  4. 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.