Paramclasses Academy - Blog

Tutorials Details

←
Post 8 of 29
→

What is State Management with the useState Hook in React.js ?

What is State Management with the useState Hook in React.js ?

October 29, 2026

1. Introduction

In React, components display information to users. Many applications also need to update that information when users interact with the interface.

For example:

  • A counter increases when a user clicks a button.

  • A like button changes its status when clicked.

  • A shopping cart updates when a product is added.

  • A form stores the values entered by a user.

  • A dropdown opens or closes when clicked.

How can React manage data that changes over time?

React provides state to store information that can change during a component's lifetime. The useState Hook is one of the most commonly used tools for managing state in functional components.

When state is updated correctly, React schedules a re-render so that the component can display the updated information.

In this tutorial, you will learn how to use useState, update state through event handlers, manage different types of state values, and avoid common mistakes.

2. Learning Objectives

By the end of this tutorial, you will be able to:

  • Understand state in React.

  • Explain what a React Hook is.

  • Import and use the useState Hook.

  • Initialize state with default values.

  • Update state using a setter function.

  • Handle state updates through button clicks and user interactions.

  • Manage numbers, strings, booleans, arrays, and objects in state.

  • Understand why state updates may not be immediately visible.

  • Avoid directly mutating state.

  • Build a practical counter application.

3. Core Concepts and Explanation

3.1 What Is State in React?

State is component data that can change over time and influence what the component renders.

For example, consider a counter that displays 0 initially and increases whenever the user clicks a button.

The counter's current value is state because it changes during interaction.

Without state, you would need another way to manage the changing value and trigger the UI to update. React state provides a standard mechanism for doing both.

3.2 What Is a Hook?

A Hook is a special React function that allows a functional component to use React features.

Some common React Hooks are:

  • useState — manages state.

  • useEffect — synchronizes a component with external systems.

  • useContext — reads a context value.

  • useRef — stores a value or references a DOM element.

In this tutorial, we will focus on useState.

3.3 Understanding the useState Syntax

First, import useState from React.

import { useState } from "react";

The basic syntax is:

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

There are three important parts:

  1. state — the current state value.

  2. setState — the function used to request a state update.

  3. initialValue — the initial value used when the component is initialized.

For example:

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

Here:

  • count initially contains 0.

  • setCount is used to update the counter.

  • 0 is the initial value.

The names count and setCount are developer-chosen variable names. The setter is conventionally named by adding set to the state variable's name.

3.4 Your First Counter Application

Create or open App.jsx in your React project.

import { useState } from "react";

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

  return (
    <div>
      <h1>Counter App</h1>
      <h2>Count: {count}</h2>

      <button onClick={() => setCount(count + 1)}>
        Increase
      </button>
    </div>
  );
}

export default App;

Expected behavior:

  • Initially, the counter displays 0.

  • Click Increase once: the counter displays 1.

  • Click Increase again: the counter displays 2.

The state setter requests an update, and React renders the component with the new state value.

3.5 Updating State with a Setter Function

You should update state through its setter function rather than directly assigning a new value to the state variable.

Incorrect:

count = count + 1;

Correct:

setCount(count + 1);

Directly assigning to the local state variable does not tell React to update the component.

For state updates based on the previous state, you can use a functional updater:

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

This is especially useful when the next value depends on the previous value.

For example, when an update is calculated from the current count, the functional form makes that dependency explicit.

3.6 Decreasing and Resetting the Counter

Let's add more controls.

import { useState } from "react";

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

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

  function decreaseCount() {
    setCount((previousCount) => previousCount - 1);
  }

  function resetCount() {
    setCount(0);
  }

  return (
    <div>
      <h1>Counter App</h1>
      <h2>Count: {count}</h2>

      <button onClick={increaseCount}>Increase</button>
      <button onClick={decreaseCount}>Decrease</button>
      <button onClick={resetCount}>Reset</button>
    </div>
  );
}

export default App;

How it works:

  • increaseCount increases the count by one.

  • decreaseCount decreases the count by one.

  • resetCount sets the count back to zero.

Notice that the functions are passed to onClick without parentheses. React calls them when the relevant button is clicked.

3.7 State with Different Data Types

State can hold any JavaScript value, including numbers, strings, booleans, arrays, and objects.

A. Number state

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

setAge(19);

Useful for counters, quantities, scores, and numeric values.

B. String state

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

setName("Amit");

Useful for names, messages, search queries, and form inputs.

C. Boolean state

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

setIsVisible(false);

Useful for showing or hiding content, opening menus, and toggling UI controls.

D. Array state

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

To add a skill without mutating the existing array:

setSkills((previousSkills) => [
  ...previousSkills,
  "React",
]);

The spread operator creates a new array containing the existing skills and the new item.

E. Object state

const [user, setUser] = useState({
  name: "Rahul",
  city: "Raipur",
});

To update the city while preserving the other property:

setUser((previousUser) => ({
  ...previousUser,
  city: "Bilaspur",
}));

The spread operator copies the existing properties, and city is replaced with the new value.

3.8 State and Conditional Rendering

State can determine which elements appear in the interface.

import { useState } from "react";

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

  return (
    <div>
      <h1>Toggle Example</h1>

      <button onClick={() => setIsVisible((value) => !value)}>
        {isVisible ? "Hide Message" : "Show Message"}
      </button>

      {isVisible && <p>Welcome to Param Classes!</p>}
    </div>
  );
}

export default App;

Initially, the message is hidden. Clicking the button toggles isVisible between true and false.

When the value is true, React renders the message. When it is false, the message is not rendered.

This pattern is commonly used for dropdowns, expandable sections, menus, and dialogs.

3.9 How State Updates Work

State updates are scheduled by React. The current state variable does not change immediately inside the already-running function.

Consider this example:

function handleClick() {
  setCount(count + 1);
  console.log(count);
}

The console.log(count) statement reads the state value from the current render. It does not necessarily show the new value requested by setCount.

React will render the component with the updated value as part of its update process.

If you need to calculate a new value from the previous one, use:

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

For multiple updates that depend on the previous value, functional updates are particularly important:

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

When these updates are processed together, the counter increases by three.

By contrast, calling setCount(count + 1) three times in the same event handler generally requests the same value three times, because each expression uses the count value from that render.

3.10 Rules of Hooks

Follow these important rules when using useState:

  1. Call Hooks at the top level of a functional component or inside a custom Hook.

  2. Do not call Hooks inside loops, conditions, or nested functions.

  3. Do not call Hooks from ordinary JavaScript functions that are not React components or custom Hooks.

  4. Use the state setter to update state.

  5. Keep each component's state logic clear and predictable.

Correct:

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

  if (count > 5) {
    // Conditional logic is fine here.
  }

  return <p>{count}</p>;
}

Incorrect:

function App({ enabled }) {
  if (enabled) {
    const [count, setCount] = useState(0);
  }

  return <p>Example</p>;
}

The Hook is inside a condition in the incorrect example, so its call order could vary between renders.

4. Practical Code Examples

Example 1: Like Button

Create a component that toggles a like status.

import { useState } from "react";

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

  return (
    <div>
      <h2>{liked ? "You liked this post!" : "Like this post"}</h2>

      <button onClick={() => setLiked((value) => !value)}>
        {liked ? "Unlike" : "Like"}
      </button>
    </div>
  );
}

export default LikeButton;

Expected behavior:

  • Initially, the button says Like.

  • After clicking it, the message says You liked this post!.

  • The button changes to Unlike.

  • Clicking it again reverses the state.

Example 2: Controlled Input Field

State can store a value entered by a user.

import { useState } from "react";

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

  return (
    <div>
      <h1>Name Form</h1>

      <input
        type="text"
        placeholder="Enter your name"
        value={name}
        onChange={(event) => setName(event.target.value)}
      />

      <p>Your name: {name || "Not entered yet"}</p>
    </div>
  );
}

export default App;

Here, value={name} connects the input to React state. The onChange handler updates state as the user types.

This is called a controlled input.

Example 3: Adding Items to an Array

import { useState } from "react";

function App() {
  const [tasks, setTasks] = useState(["Learn HTML", "Learn CSS"]);

  function addTask() {
    setTasks((previousTasks) => [
      ...previousTasks,
      "Learn React",
    ]);
  }

  return (
    <div>
      <h1>My Tasks</h1>

      <ul>
        {tasks.map((task) => (
          <li key={task}>{task}</li>
        ))}
      </ul>

      <button onClick={addTask}>Add React Task</button>
    </div>
  );
}

export default App;

The button adds Learn React to the list. For this simple example, the task strings are unique. In a real task application, use stable unique IDs for list keys and prevent accidental duplicate additions if needed.

Example 4: Updating an Object in State

import { useState } from "react";

function App() {
  const [user, setUser] = useState({
    name: "Rahul",
    city: "Raipur",
  });

  function changeCity() {
    setUser((previousUser) => ({
      ...previousUser,
      city: "Bilaspur",
    }));
  }

  return (
    <div>
      <h1>User Profile</h1>
      <p>Name: {user.name}</p>
      <p>City: {user.city}</p>

      <button onClick={changeCity}>Change City</button>
    </div>
  );
}

export default App;

When the button is clicked, the city changes while the name remains the same.

Remember that the spread operation creates a shallow copy. For deeply nested objects, you must also copy the nested objects or arrays you need to update.

5. Real-World Applications

The useState Hook is useful in many applications.

E-commerce

  • Store the selected quantity of a product.

  • Show or hide product filters.

  • Track whether a local cart panel is open.

  • Store search input and selected sorting options.

Forms

  • Store names, email addresses, and other input values.

  • Display validation messages.

  • Track whether a form is submitting or has been completed.

Admin dashboards

  • Open and close menus.

  • Switch between tabs.

  • Store selected filters and page numbers.

Social applications

  • Toggle like buttons.

  • Show and hide comments.

  • Store text being composed for a post.

Important: State stored with useState belongs to the component instance. It does not automatically persist after a page reload or become shared global state across the application.

6. Common Mistakes and Best Practices

Mistake 1: Updating the State Variable Directly

Incorrect:

count = count + 1;

Correct:

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

Always use the setter to request state updates.

Mistake 2: Mutating an Array

Incorrect:

tasks.push("Learn React");
setTasks(tasks);

This mutates the existing array rather than creating a new one.

Correct:

setTasks((previousTasks) => [
  ...previousTasks,
  "Learn React",
]);

Mistake 3: Replacing an Object Without Preserving Its Other Properties

Incorrect when you want to keep the existing name:

setUser({ city: "Bilaspur" });

Correct:

setUser((previousUser) => ({
  ...previousUser,
  city: "Bilaspur",
}));

Mistake 4: Expecting the State Variable to Change Immediately

Incorrect assumption:

setCount(count + 1);
console.log(count); // Still reads this render's value

React schedules the update. The current function continues to use the state snapshot from its render.

Mistake 5: Calling a Handler During Rendering

Incorrect:

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

Correct:

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

Passing the function reference lets React invoke the handler when the event occurs.

Best Practices

  • Use descriptive state names, such as isOpen, username, or selectedProduct.

  • Use functional updates when the next state depends on the previous state.

  • Treat objects and arrays in state as immutable.

  • Keep state as simple as practical.

  • Avoid storing values in state when they can be calculated from existing props or state during rendering.

  • Keep related state together when it makes the logic easier to maintain.

  • Split unrelated state into separate variables when that improves clarity.

  • Follow the Rules of Hooks.

7. Practice Exercises

Try to complete these exercises without copying the examples.

Exercise 1: Counter

Build a counter with Increase, Decrease, and Reset buttons.

Exercise 2: Name Input

Create an input field that displays the user's name as they type.

Exercise 3: Visibility Toggle

Create a button that shows or hides a paragraph.

Exercise 4: Like Button

Build a button that toggles between Like and Unlike.

Exercise 5: Skills List

Store a list of skills in state and add another skill when a button is clicked.

Exercise 6: User Profile

Store a user's name and city in an object. Add a button that changes the city without losing the name.

Exercise 7: Simple Task List

Store tasks in an array and render them using map(). Add a new task when the user clicks a button.

Self-check Questions

  1. What is state in React?

  2. What does the useState Hook return?

  3. What is the purpose of a state setter?

  4. Why should you avoid directly modifying state?

  5. When should you use a functional state update?

  6. How do you store arrays and objects in state?

  7. Why does React re-render a component after a state update?

  8. What are the Rules of Hooks?

  9. What is a controlled input?

  10. How is state different from props?

8. Key Takeaways and Conclusion

In this tutorial, you learned that:

  • State represents data that can change over time.

  • useState allows functional components to manage state.

  • The Hook returns the current state value and a setter function.

  • State updates can trigger React to render the component again.

  • Functional updates are useful when new state depends on previous state.

  • State can hold numbers, strings, booleans, arrays, and objects.

  • Arrays and objects should be updated immutably.

  • State is useful for counters, forms, toggles, lists, and interactive interfaces.

  • Hooks must follow the Rules of Hooks.

Final takeaway: useState is one of the foundations of interactive React development. Practice building small components with changing data before moving on to more complex state-management techniques.

←
Post 8 of 29
→