JP
JP Codes
Change theme
React

useState vs useEffect Explained

Two of React's most fundamental hooks — but what's the real difference? When do you reach for useState, and when does useEffect belong? A clear, practical breakdown with code examples.

James Platt

James Platt

November 11, 2025 · 8 min read

8 min read
useState vs useEffect

When you start learning React, two hooks dominate almost every component you write: useState and useEffect. They look simple at first glance, but the distinction between them is fundamental — and misunderstanding when to use each is one of the most common sources of bugs and over-complicated code.

What is useState?

useState is React's mechanism for storing and updating values that belong to a component. When state changes, the component re-renders, and the UI reflects the new value. It's synchronous in terms of triggering a re-render, and its sole purpose is to track data that the component owns.

import { useState } from 'react'

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

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

In this example, count is the current state value, setCount is the function to update it, and 0 is the initial value. Every time you click the button, setCount triggers a re-render with the updated count.

Key characteristics of useState

What is useEffect?

useEffect is for side effects — operations that need to happen outside of the render cycle. This includes fetching data, subscribing to events, setting up timers, directly manipulating the DOM, and integrating with third-party libraries.

import { useState, useEffect } from 'react'

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

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

  if (!user) return <p>Loading...</p>
  return <p>Hello, {user.name}</p>
}

The second argument to useEffect — the dependency array — controls when the effect runs. If userId changes, the effect re-runs and fetches new data. This is the critical pattern for synchronising your component with external data sources.

The dependency array

The Core Difference

useState answers: "What data does this component need to track?"
useEffect answers: "What should happen in response to the component mounting, updating, or unmounting?"

Put another way: useState is about what your component knows. useEffect is about what your component does as a consequence of rendering.

Quick Comparison

Feature useState useEffect
Purpose Store & update component state Handle side effects
When it runs When setter is called After render, based on deps
Triggers re-render Yes Not directly
Use for UI data, form inputs, toggles Data fetching, subscriptions, timers
Cleanup Not applicable Return a cleanup function

Common Patterns

Fetching data on mount

useEffect(() => {
  fetchPosts().then(data => setPosts(data))
}, []) // empty array = runs once on mount

Responding to a prop change

useEffect(() => {
  setFilteredItems(items.filter(i => i.tag === selectedTag))
}, [items, selectedTag])

Cleanup — unsubscribing

useEffect(() => {
  const subscription = someEvent.subscribe(handler)
  return () => subscription.unsubscribe() // cleanup on unmount
}, [])

A Practical Rule of Thumb

If you're tracking a value that your component needs to display or compute from — use useState. If you're doing something that interacts with the outside world (the network, the DOM, a timer, a subscription) — use useEffect. Often you'll use both together: fetch data in an effect, store it in state, and display it in the render.

One thing to avoid: don't use useEffect to transform state that could be derived during render. If you can compute a value directly from existing state or props without side effects, do it during the render — no hook needed.

Tags

James Platt

James Platt

Web Developer

James is a Microsoft-qualified C# .NET developer with extensive experience building robust, data-rich web applications. He writes about web development, software architecture, and best practices at JP Codes.

Read more about James

More articles