Skip to main content

Command Palette

Search for a command to run...

React State & useState – Making Components Interactive

Updated
11 min readView as Markdown

React becomes truly powerful when our UI isn't just displaying static information but can respond to user actions and change dynamically.

A button can change a counter.

A form can update its input.

A menu can open and close.

A like button can change from ❤️ to 🤍.

All of this requires State.

Today, we'll understand:

  • What is State?

  • Why do we need State?

  • State vs Props

  • useState

  • Updating State

  • Multiple State variables

  • State with objects and arrays

  • Functional state updates

  • Why we should never mutate state directly

  • Common mistakes

  • Best practices


🧠 What Is State in React?

State is data that belongs to a component and can change over time.

For example:

function Counter() {
  let count = 0;

  return (
    <div>
      <p>Count: {count}</p>
      <button>Increase</button>
    </div>
  );
}

We have a variable called count.

But if we change it:

count++;

React doesn't automatically know that it needs to update the UI.

That's where State comes in.


🔄 Why Do We Need State?

Consider a counter:

Count: 0

[ Increase ]

After clicking:

Count: 1

[ Increase ]

And again:

Count: 2

[ Increase ]

The data is changing.

Therefore, React needs a way to:

  1. Store changing data

  2. Detect that the data changed

  3. Re-render the component

  4. Display the updated value

React provides this through State.


⚛️ useState

useState is a React Hook that allows functional components to have state.

Basic syntax:

const [state, setState] = useState(initialValue);

For example:

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>
  );
}

export default Counter;

Let's break this down.


🔍 Understanding useState

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

There are three important parts.

1. count

This is the current state value.

count

Initially:

0

2. setCount

This is the function used to update the state.

setCount(1);

or:

setCount(count + 1);

3. 0

This is the initial value.

useState(0);

So:

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

means:

Create a state variable called count, initially set to 0, and give me a function called setCount to update it.


🔁 What Happens When State Changes?

Suppose:

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

Initially:

count = 0

User clicks:

setCount(1);

React schedules the state update.

The component renders again.

Now:

count = 1

The UI becomes:

Count: 1

This is the basic React update cycle:

User Action
     ↓
State Update
     ↓
React Re-render
     ↓
Updated UI

🖱️ Handling Events With State

State becomes especially useful when combined with events.

Example:

import { useState } from "react";

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

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

  return (
    <>
      <h2>{count}</h2>

      <button onClick={increaseCount}>
        Increase
      </button>
    </>
  );
}

When the button is clicked:

increaseCount

runs.

Then:

setCount(count + 1);

updates the state.


⚠️ Important: Don't Call the Function Immediately

Correct:

<button onClick={increaseCount}>
  Increase
</button>

Incorrect:

<button onClick={increaseCount()}>
  Increase
</button>

Why?

Because:

increaseCount()

calls the function immediately during rendering.

Instead, React needs a function reference that it can call when the event happens.


➕ Incrementing State

A simple approach is:

setCount(count + 1);

For example:

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

This works when the next state depends on the current state.

However, there's an even safer pattern.


🧮 Functional State Updates

When the new state depends on the previous state, use a function:

setCount(previousCount => previousCount + 1);

Example:

function increase() {
  setCount(previousCount => previousCount + 1);
}

Here:

previousCount

represents the latest state value available to React.

This becomes especially important when performing multiple updates.

For example:

setCount(count + 1);
setCount(count + 1);

This does not necessarily produce the result you might expect.

Instead:

setCount(previous => previous + 1);
setCount(previous => previous + 1);

allows each update to build on the previous one.


🧩 Multiple State Variables

A component can have multiple pieces of state.

import { useState } from "react";

function Profile() {
  const [name, setName] = useState("Saurabh");
  const [age, setAge] = useState(23);

  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
    </div>
  );
}

We can update them independently:

setName("Rahul");

and:

setAge(24);

Each state variable manages its own value.


🔢 State Can Store Different Data Types

State isn't limited to numbers.

It can store:

String

const [name, setName] = useState("Saurabh");

Number

const [age, setAge] = useState(23);

Boolean

const [isLoggedIn, setIsLoggedIn] = useState(false);

Array

const [skills, setSkills] = useState(["HTML", "CSS"]);

Object

const [user, setUser] = useState({
  name: "Saurabh",
  age: 23
});

🟢 Boolean State

Boolean state is extremely useful for UI interactions.

For example:

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

Button:

<button onClick={() => setIsVisible(!isVisible)}>
  Toggle
</button>

Then:

{isVisible && <p>Hello React!</p>}

Now clicking the button toggles the content.


📝 Example: Show / Hide Password

import { useState } from "react";

function Password() {
  const [showPassword, setShowPassword] = useState(false);

  return (
    <div>
      <input
        type={showPassword ? "text" : "password"}
        placeholder="Password"
      />

      <button onClick={() => setShowPassword(!showPassword)}>
        {showPassword ? "Hide" : "Show"}
      </button>
    </div>
  );
}

Here state controls:

type={showPassword ? "text" : "password"}

This is a great example of state controlling UI.


📦 State With Objects

Suppose we have:

const [user, setUser] = useState({
  name: "Saurabh",
  age: 23
});

We should not directly modify the object.

❌ Don't do this:

user.name = "Rahul";

Instead, create a new object:

setUser({
  ...user,
  name: "Rahul"
});

The spread operator copies the existing properties.

So:

{
  ...user,
  name: "Rahul"
}

means:

Keep the existing user data, but replace name.


📋 State With Arrays

Suppose:

const [skills, setSkills] = useState([
  "HTML",
  "CSS"
]);

To add JavaScript:

setSkills([
  ...skills,
  "JavaScript"
]);

Now:

[
  "HTML",
  "CSS",
  "JavaScript"
]

Again, we're creating a new array instead of modifying the existing one.


❌ Don't Mutate State Directly

This is one of the most important rules.

Avoid:

skills.push("React");

or:

user.name = "Rahul";

Instead:

setSkills([...skills, "React"]);

and:

setUser({
  ...user,
  name: "Rahul"
});

Think of state as something you should replace with a new value, rather than directly modifying.


⚛️ Props vs State

This is extremely important.

Props State
Passed from parent Managed by component
Read-only Updated using setter
Used to pass data Used for changing data
Parent controls the value Component manages the value
Helps component communication Helps component interaction

Example:

function User({ name }) {
  const [age, setAge] = useState(23);

  return (
    <div>
      <h2>{name}</h2>
      <p>{age}</p>
    </div>
  );
}

Here:

name

is a prop.

While:

age

is state.


🔄 Props + State Together

This is where React becomes really powerful.

Parent:

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

  return <Profile name={name} />;
}

Child:

function Profile({ name }) {
  return <h2>{name}</h2>;
}

The flow is:

App State
   ↓
Props
   ↓
Profile Component
   ↓
UI

This is the foundation of React's one-way data flow.


🛒 Real-World Example: Shopping Cart

Imagine a shopping cart.

const [cartItems, setCartItems] = useState([]);

When a product is added:

setCartItems([
  ...cartItems,
  product
]);

When removed, we could create a new array using:

setCartItems(
  cartItems.filter(item => item.id !== product.id)
);

State is therefore useful for things like:

  • Shopping carts

  • Login status

  • Theme settings

  • Counters

  • Forms

  • Likes

  • Modals

  • Menus

  • Filters

  • Search results


🧠 The Most Important Mental Model

Don't think:

"I changed a variable."

Think:

"I requested React to update state."

For example:

setCount(count + 1);

The setter tells React:

State changed
     ↓
Component needs to render again
     ↓
React calculates the updated UI
     ↓
Browser displays the changes

⚠️ Common Mistakes

Mistake 1 — Direct mutation

count++;

setCount(count + 1);

Mistake 2 — Calling the event handler immediately

onClick={increase()}

onClick={increase}

Mistake 3 — Mutating arrays

items.push(newItem);

setItems([...items, newItem]);

Mistake 4 — Mutating objects

user.name = "Rahul";

setUser({
  ...user,
  name: "Rahul"
});

Mistake 5 — Using state for everything

Not every variable needs to be state.

If a value doesn't need to change the UI when it changes, it may not need state.

For example:

const appName = "My App";

doesn't need to be state just because it is a variable.


🏆 Best Practices

1. Keep state minimal

Only store information that genuinely needs to be state.

2. Use meaningful names

Prefer:

const [isLoggedIn, setIsLoggedIn] = useState(false);

over:

const [x, setX] = useState(false);

3. Use functional updates when appropriate

setCount(prev => prev + 1);

4. Never mutate state directly

Create new arrays/objects instead.

Don't unnecessarily create dozens of unrelated state variables.

6. Remember that state updates trigger rendering

Changing state isn't just changing a JavaScript variable — it affects the component's UI.


🚀 Complete Counter Example

Here's everything together:

import { useState } from "react";

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

  function increase() {
    setCount(prev => prev + 1);
  }

  function decrease() {
    setCount(prev => prev - 1);
  }

  function reset() {
    setCount(0);
  }

  return (
    <div>
      <h1>Counter</h1>

      <h2>{count}</h2>

      <button onClick={increase}>
        +
      </button>

      <button onClick={decrease}>
        -
      </button>

      <button onClick={reset}>
        Reset
      </button>
    </div>
  );
}

export default Counter;

This small application demonstrates the core idea of React State:

State
 ↓
UI

User Action
 ↓
State Update
 ↓
Re-render
 ↓
Updated UI

💡 My Biggest Takeaway

Today's biggest lesson for me was understanding that React State is what makes components dynamic and interactive.

Props allow components to receive data.

State allows components to manage changing data.

The most important pattern I learned today is:

const [value, setValue] = useState(initialValue);

And whenever the UI needs to respond to changing data:

setValue(newValue);

React takes care of updating the UI.

This is a major step forward from simply writing static components.


🎯 Final Summary

Today I learned:

  • What React State is

  • Why State is required

  • useState

  • State variables

  • State setter functions

  • Updating state

  • Functional state updates

  • Boolean state

  • Object state

  • Array state

  • State immutability

  • Props vs State

  • State + Props together

  • How state causes UI updates

  • Common mistakes

  • Best practices

React is starting to make much more sense now.

Props help components communicate.

State helps components remember and react to changes.

And together, they form the foundation of interactive React applications. 🚀

11 views

100 Days of Code: My Journey to Becoming a Full Stack Developer

Part 1 of 50

Welcome to my 100 Days of Code journey! In this series, I'll document my daily progress as I learn Full Stack Web Development from the ground up. Every post will cover what I learned, challenges I faced, mistakes I made, and the projects I built. My goal is not just to complete 100 days but to become a better developer through consistency, discipline, and learning in public. Topics I'll cover include: • Git & GitHub • HTML, CSS & JavaScript • React.js • Node.js & Express • MongoDB • APIs • Real-world Projects • AI tools for Developers Whether you're just starting out or revising your fundamentals, I hope this journey helps you learn alongside me. Let's build, learn, and grow together! 🚀

More from this blog

T

TheSaurceCode

73 posts

Documenting my journey to becoming a Full Stack Developer through daily blogs, coding challenges, projects, tutorials, and lessons learned. Learn, build, and grow with me