What is the difference between useState and useEffect in React?

useState stores and updates component state that drives re-renders; useEffect runs side effects (data fetching, subscriptions) after renders.

React

Overview

`useState` and `useEffect` are the two most-used React hooks. `useState` manages data that the component needs to remember between renders. `useEffect` handles side effects things that happen outside the React render cycle, like fetching data, setting timers, or subscribing to events.

Key Points

  • •`useState(initial)` returns `[value, setter]`. Calling the setter triggers a re-render.
  • •`useEffect(fn, deps)` runs `fn` after every render where a dependency changed.
  • •Empty deps `[]`: runs once after mount. No deps array: runs after every render.
  • •Return a cleanup function from `useEffect` to cancel subscriptions or timers on unmount.
  • •Do not update state inside `useEffect` without a condition it causes infinite loops.

Examples

useState: counter

tsx
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Clicked {count} times
    </button>
  );
}

useEffect: fetch data on mount

tsx
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    let cancelled = false;
    fetch(`/api/users/${userId}`)
      .then(r => r.json())
      .then(data => { if (!cancelled) setUser(data); });
    return () => { cancelled = true; }; // cleanup on unmount or userId change
  }, [userId]); // re-fetch when userId changes

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

useEffect with cleanup: event listener

tsx
useEffect(() => {
  const handler = (e: KeyboardEvent) => {
    if (e.key === 'Escape') closeModal();
  };
  window.addEventListener('keydown', handler);
  return () => window.removeEventListener('keydown', handler); // cleanup
}, []);

Pro Tip

A missing dependency in the deps array is one of the most common React bugs it causes stale closures. Use the `eslint-plugin-react-hooks` exhaustive-deps rule to catch them automatically.