Understanding React Hooks: useState and useEffect Explained Simply

Understanding React Hooks: useState and useEffect Explained Simply

1 4 14
calendar_today agoschedule2 min read

Introduction

Hooks confused me for longer than I'd like to admit. Not the syntax — the syntax is simple. What confused me was when to use useEffect versus just writing normal logic in my component.

If you're at that stage right now, this article is for you. We're keeping this practical — real examples, real mistakes, no theory-heavy explanations of React's internal fiber architecture.

useState: Giving Your Component Memory

Without state, a component renders once and forgets everything. useState lets a component remember values between renders — and re-render automatically when that value changes.

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

count is the current value. setCount is the function you call to update it. Calling setCount tells React "re-render this component with the new value" — you never update count directly.

Common Mistake: Updating State Based on Old State

This looks fine but can cause bugs:

setCount(count + 1);
setCount(count + 1); // still only adds 1, not 2!

Fix it with the function form:

setCount(prev => prev + 1);
setCount(prev => prev + 1); // now correctly adds 2

This matters more than it seems — always use the function form when your new state depends on the previous state.

useEffect: Running Code at the Right Time

useEffect lets you run code in response to a component rendering, or a specific value changing — things like fetching data, updating the page title, or setting up an event listener.

import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`https://api.example.com/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]);

  if (!user) return <p>Loading...</p>;
  return <h2>{user.name}</h2>;
}

The array at the end — [userId] — is the dependency array. It tells React "only re-run this effect when userId changes."

The Three Dependency Array Patterns

useEffect(() => { /* code */ });          // runs after EVERY render
useEffect(() => { /* code */ }, []);       // runs ONCE, after the first render
useEffect(() => { /* code */ }, [userId]); // runs when userId changes

Beginners almost always want one of the last two. An empty array [] is for one-time setup (like fetching initial data). A populated array is for "re-run this when X changes."

Cleaning Up After Effects

Some effects need cleanup — like removing an event listener when the component unmounts:

useEffect(() => {
  const handleResize = () => console.log(window.innerWidth);
  window.addEventListener('resize', handleResize);

  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);

The function you return inside useEffect is the cleanup function. React calls it automatically before the component unmounts, or before the effect runs again.

Common Beginner Mistakes

  1. Forgetting the dependency array entirely. This causes the effect to run after every single render, often triggering infinite fetch loops.
  2. Missing a dependency. If your effect uses a variable from outside it, add it to the array — ESLint's react-hooks/exhaustive-deps rule will warn you about this.
  3. Putting everything in useEffect. Not all logic needs to live in an effect. If you're just calculating a value from props or state, do it directly in the component body — no hook needed.

Final Thoughts

useState gives your component memory. useEffect lets it react to changes and the outside world (APIs, timers, event listeners). Once you separate those two jobs clearly in your head, hooks stop feeling mysterious.

Try rebuilding a small feature — a search box with an API call, for example — using exactly these two hooks. That's usually the exercise that makes it click.


Muhammad Farhan is a Frontend Developer specializing in React.js and Tailwind CSS, based in Dera Ismail Khan, Pakistan.
Portfolio: muhammad-farhan-dev.github.io/muhammadfarhan.dev

🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

5 Web Dev Pitfalls That Are Silently Killing Your Projects (With Real Fixes)

Dharanidharan - Mar 3

React Native Quote Audit - USA

kajolshah - Mar 2

Tailwind CSS for Beginners: From Zero to Your First Component

muhammadfarhan.dev - Jul 21

15 Free Frontend Tools Every Developer Should Know in 2026

muhammadfarhan.dev - Jul 14
chevron_left
1.2k Points19 Badges
Dera Ismail Khan, Pakistanmuhammadfarhandev.netlify.app
17Posts
6Comments
9Connections
I'm a passionate Frontend Developer from Dera Ismail Khan , Pakistan, with 2+ years of hands-on expe... Show more

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!