DEV Community

DHANRAJ S
DHANRAJ S

Posted on

Why We Use useState in React — Explained Simply with Real Examples

Hey!

Let me ask you something before we start.

You built a counter in plain JavaScript. You clicked a button. The number went up. It worked.

So when you moved to React — you tried the same thing.

function Counter() {
  let count = 0;

  function handleClick() {
    count = count + 1;
    console.log(count); // this works fine
  }

  return (
    <div>
      <p>{count}</p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

You click the button. The console shows the number going up. But the screen? The screen stays at 0.

Nothing changes on the page.

And you wonder — what is going on?

That confusion is exactly why useState exists. And by the end of this blog, you will fully understand it.


1. Why Does the Screen Not Update?

Here is the thing about React.

React does not watch your normal variables. When you change let count = 0 — React has no idea that anything changed. So it does not re-render the component. The screen stays the same.

Think of it like this.

Imagine you are writing numbers on a whiteboard. You erase 0 and write 1. But the camera recording the whiteboard does not know you changed it — because nobody told the camera to look again.

React is the camera. Your variable change is you erasing and writing. React needs to be told — "hey, something changed, please look again and update the screen."

That is exactly what useState does.


2. What Is useState?

useState is a React hook that does two things:

  • It stores a value (your state)
  • It gives you a function to update that value — and when you use that function, React automatically re-renders the component and updates the screen

That is it. That is the whole idea.

Let us see the syntax first.

import { useState } from "react";

const [value, setValue] = useState(initialValue);
Enter fullscreen mode Exit fullscreen mode

Let us break this down piece by piece.

useState(initialValue) — you call useState and pass the starting value. This can be a number, string, boolean, array, object — anything.

value — this is the current state. Whatever is stored right now.

setValue — this is the function to update the state. When you call this, React updates the value AND re-renders the component.

const [value, setValue] — this is called array destructuring. useState returns two things in an array. You unpack them into two separate variables in one line.

Quick question for you.

Can you name the two things useState always gives you back?

The current value. And the function to update it. Always. Every time.


3. Fix the Counter — Using useState

Now let us fix the broken counter from the beginning.

import { useState } from "react";

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

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now when you click the button:

  • setCount(count + 1) is called
  • React updates count to the new value
  • React re-renders the component
  • The screen shows the updated number

The screen actually changes now. That is the difference.

Let us trace through exactly what happens step by step:

Page loads
  → count = 0
  → Screen shows: Count: 0

User clicks button
  → handleClick runs
  → setCount(0 + 1) is called
  → React updates count to 1
  → React re-renders Counter
  → Screen shows: Count: 1

User clicks again
  → setCount(1 + 1) is called
  → React updates count to 2
  → Screen shows: Count: 2
Enter fullscreen mode Exit fullscreen mode

Every click triggers a re-render. Every re-render shows the latest value. That is useState working.


4. The Naming Convention

Before we go further — notice the naming pattern.

const [count, setCount] = useState(0);
const [name, setName] = useState("");
const [isOpen, setIsOpen] = useState(false);
Enter fullscreen mode Exit fullscreen mode

The update function always starts with set followed by the state name.

count and setCount.
name and setName.
isOpen and setIsOpen.

This is not a rule enforced by React. It is a convention the whole React community follows. Stick to it — your code will be easier to read.


5. Example 1 — Toggle Show and Hide

Let us build something more practical.

A button that shows and hides a message.

import { useState } from "react";

function ToggleMessage() {
  const [isVisible, setIsVisible] = useState(false);

  function handleToggle() {
    setIsVisible(!isVisible);
  }

  return (
    <div>
      <button onClick={handleToggle}>
        {isVisible ? "Hide Message" : "Show Message"}
      </button>

      {isVisible && <p>Hello! I am visible now.</p>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Quick question for you.

What does !isVisible do?

If isVisible is true!isVisible makes it false.
If isVisible is false!isVisible makes it true.

It flips the value every time. One click shows. Next click hides. Next click shows again.

And the button text changes too — {isVisible ? "Hide Message" : "Show Message"}. That is a ternary operator. If visible, show "Hide Message". If not, show "Show Message".

The UI reacts to the state. That is why it is called React.


6. Example 2 — Input Field

This one is used in almost every real React app.

Capturing what a user types in an input field.

import { useState } from "react";

function NameInput() {
  const [name, setName] = useState("");

  function handleChange(event) {
    setName(event.target.value);
  }

  return (
    <div>
      <input
        type="text"
        placeholder="Enter your name"
        value={name}
        onChange={handleChange}
      />
      <p>Hello, {name}!</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Every time you type a letter:

  • onChange fires
  • handleChange runs
  • event.target.value gets the current text in the input
  • setName updates the state
  • React re-renders
  • The <p> below shows the updated name instantly

Type "R" — screen shows "Hello, R!"
Type "Ra" — screen shows "Hello, Ra!"
Type "Ravi" — screen shows "Hello, Ravi!"

This pattern is called a controlled input. The input's value is always controlled by React state. It is the standard way to handle forms in React.


7. Example 3 — Like Button

Something you have seen on every social media app.

import { useState } from "react";

function LikeButton() {
  const [liked, setLiked] = useState(false);
  const [likeCount, setLikeCount] = useState(0);

  function handleLike() {
    if (!liked) {
      setLiked(true);
      setLikeCount(likeCount + 1);
    } else {
      setLiked(false);
      setLikeCount(likeCount - 1);
    }
  }

  return (
    <div>
      <button onClick={handleLike}>
        {liked ? "Unlike" : "Like"}
      </button>
      <p>{likeCount} Likes</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Notice something here — one component has two useState calls.

const [liked, setLiked] = useState(false);
const [likeCount, setLikeCount] = useState(0);
Enter fullscreen mode Exit fullscreen mode

You can use as many useState hooks as you need in one component. Each one is completely independent.

liked tracks whether you liked or not.
likeCount tracks the number.

Click once — liked becomes true, count goes up by 1.
Click again — liked becomes false, count goes down by 1.


8. Example 4 — Background Color Changer

Let us do something visual.

import { useState } from "react";

function ColorChanger() {
  const [color, setColor] = useState("white");

  return (
    <div style={{ backgroundColor: color, padding: "40px", textAlign: "center" }}>
      <p>Current color: {color}</p>
      <button onClick={() => setColor("tomato")}>Red</button>
      <button onClick={() => setColor("steelblue")}>Blue</button>
      <button onClick={() => setColor("mediumseagreen")}>Green</button>
      <button onClick={() => setColor("white")}>Reset</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

State here is a string — the color name.

Every time you click a button, setColor updates the state to a new color string. React re-renders. The background changes instantly.

This shows that state is not just for numbers and booleans. It can hold any value — strings, arrays, objects — anything your UI needs to track.


9. One Important Rule — Never Mutate State Directly

This is a mistake many beginners make.

// WRONG — never do this
count = count + 1;

// WRONG — never do this either
myArray.push(newItem);

// RIGHT — always use the setter function
setCount(count + 1);

// RIGHT — for arrays, create a new array
setMyArray([...myArray, newItem]);
Enter fullscreen mode Exit fullscreen mode

Why?

Because if you change state directly — React does not know it changed. No re-render happens. The screen does not update.

Always use the setter function. Always. That is the only way React knows something changed and needs to re-render.


10. What Happens When State Changes — The Full Picture

Let us put it all together.

Component renders for the first time
  → useState gives you the initial value
  → Component displays that value on screen

User does something (click, type, hover)
  → You call the setter function with a new value
  → React schedules a re-render

React re-renders the component
  → useState now gives you the updated value
  → Component displays the new value on screen

User does something again
  → The cycle repeats
Enter fullscreen mode Exit fullscreen mode

This cycle — render, interact, update, re-render — is the core of how React works. And useState is what drives it.


Quick Summary — 5 Things to Remember

  1. Normal variables do not trigger re-renders — React does not watch them. Changing a normal variable does nothing to the screen.

  2. useState stores a value and gives you a setterconst [value, setValue] = useState(initialValue). The setter is the only right way to update state.

  3. Calling the setter re-renders the component — React updates the value and redraws the component automatically.

  4. You can use multiple useState in one component — each one is independent. Use as many as your component needs.

  5. Never update state directly — always use the setter function. Direct mutation skips React and the screen will not update.


useState is the first hook every React developer learns. And once it clicks — the way you think about building UIs changes completely.

You stop thinking about DOM manipulation. You start thinking about state — what data does this component need to track, and how should the UI look based on that data.

That shift in thinking is what React is really about.

Try building these examples yourself. Change the initial values. Add new buttons. Break things and fix them. That hands-on time will teach you more than any blog can.

If you have a question or something did not click — drop it in the comments below. Happy to explain it differently.


Thanks for reading. Keep building.

Top comments (0)