DEV Community

Marvin Roque
Marvin Roque

Posted on

1

React State: useState Explained Simply

Look, I get it. React state is confusing at first. I spent way too many hours wondering why my components weren't updating properly. Let me save you that headache and break down useState in a way that actually makes sense.

What Even is State?

It's just data that changes. That's it. Think of it like variables that React watches. When they change, your component updates.

Here's a dead-simple counter:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  // count = the number
  // setCount = how we change it

  return (
    <div>
      <p>Clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        +1
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Updating State (The Way That Actually Works)

I kept messing this up when I started. Here's what not to do:

// 🤦‍♂️ This bit me so many times
const [score, setScore] = useState(0);
setScore(score + 1);
setScore(score + 1);
// Only goes up by 1... why??
Enter fullscreen mode Exit fullscreen mode

Here's how to fix it:

const [score, setScore] = useState(0);
// This actually works
setScore(oldScore => oldScore + 1);
setScore(oldScore => oldScore + 1);
// Now it goes up by 2!
Enter fullscreen mode Exit fullscreen mode

Stuff I Keep Messing Up

Objects Are Tricky

// Don't do what I did:
const [user, setUser] = useState({ name: 'Sam', level: 1 });
user.level = 2;  // Looks right, but doesn't work!

// Do this instead:
setUser({ ...user, level: 2 });  // This works
Enter fullscreen mode Exit fullscreen mode

Checking New Values

// This drove me nuts for hours:
setCount(count + 1);
console.log(count);  // Still the old number!

// What actually works:
setCount(count + 1);
useEffect(() => {
  console.log(count);  // Now we see the new number
}, [count]);
Enter fullscreen mode Exit fullscreen mode

Heavy Calculations

// This slows things down:
const [data, setData] = useState(heavyCalculation());

// Much better:
const [data, setData] = useState(() => heavyCalculation());
Enter fullscreen mode Exit fullscreen mode

Quick Things I Learned The Hard Way

  • Use prev => ... when updating based on old values
  • Never put useState in if statements
  • Keep it simple - split big state objects if they get messy
  • Group related state together in one object

Put It All Together

Here's a real example I use a lot:

function GameScore() {
  const [game, setGame] = useState({
    score: 0,
    highScore: 0
  });

  const addPoints = (points) => {
    setGame(prev => ({
      ...prev,
      score: prev.score + points,
      highScore: Math.max(prev.highScore, prev.score + points)
    }));
  };

  return (
    <div>
      <p>Current: {game.score}</p>
      <p>Best: {game.highScore}</p>
      <button onClick={() => addPoints(10)}>
        Get Points
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

That's pretty much it! Hit me up if you have questions. I'll probably add more examples as I think of them.

Follow if this helped! More real-world React tips coming soon 🚀

Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more