Day 9: Understanding useState React Hook
5 mins read

Day 9: Understanding useState React Hook

In this blog, we’ll take a deep dive into useState React Hook and learn how to work with arrays, objects, and multiple state variables.

State is one of the most powerful features of React. By now, you’ve already used useState for toggling UI (like showing/hiding content) or handling simple values (like counters). But real-world applications are rarely that simple.

This will prepare you for building more interactive and data-driven React apps.


What are React Hooks?

Before we go deeper into useState, let’s quickly understand the bigger picture.

Hooks are special functions in React that let us “hook into” React’s features — like state and lifecycle methods — directly inside functional components.

Before React introduced Hooks (in version 16.8), developers often had to use class components for managing state or side effects. Hooks changed that by allowing functional components to be just as powerful, but with cleaner and simpler code.

👉 In simple words:

  • Without Hooks → functional components were only for UI, not logic.
  • With Hooks → functional components can handle state, lifecycle, context, refs, and more.

There are many built-in hooks, such as:

  • useState – for managing state
  • useEffect – for side effects (like fetching data)
  • useRef – for accessing DOM elements or persisting values
  • useReducer, useContext, etc.

In this blog, we’ll explore useState — our very first Hook in React.


Quick Recap: What is useState?

  • useState is a React Hook used to store and update state in functional components.
  • It returns an array with two elements:
    1. The current state value.
    2. A function to update that value.

Example:

import React, { useState } from "react";

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

  return (
    <div>
      <p>Current Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

That was the basics. Now let’s go deeper.


Working with Multiple States

Instead of one state, a component can have multiple independent states.

Example:

import React, { useState } from "react";

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

  return (
    <div>
      <p>Name: {name}</p>
      <p>Age: {age}</p>
      <button onClick={() => setAge(age + 1)}>Increase Age</button>
      <button onClick={() => setName("John")}>Change Name</button>
    </div>
  );
}

👉 This makes the code cleaner, as each piece of state has its own setter function.


useState with Arrays

Often, we need to store lists of items (like a todo list or product catalog). Let’s see how to manage arrays with useState.

import React, { useState } from "react";

function TodoList() {
  const [todos, setTodos] = useState(["Learn React", "Read a Book"]);

  const addTodo = () => {
    setTodos([...todos, "New Task"]); 
  };

  return (
    <div>
      <h2>My Todos</h2>
      <ul>
        {todos.map((todo, index) => (
          <li key={index}>{todo}</li>
        ))}
      </ul>
      <button onClick={addTodo}>Add Todo</button>
    </div>
  );
}
  • Never modify arrays directly (e.g., todos.push("New Task")).
  • Instead, create a new array using spread operator (...) or methods like filter(), map().
  • React updates UI only when state is replaced with a new reference.

useState with Objects

Working with objects in state is similar, but you must be careful not to overwrite other properties.

Example:

import React, { useState } from "react";

function UserForm() {
  const [user, setUser] = useState({ name: "Alice", email: "alice@example.com" });

  const updateName = () => {
    setUser({ ...user, name: "Bob" }); // spread old state, update name
  };

  const updateEmail = () => {
    setUser({ ...user, email: "bob@example.com" });
  };

  return (
    <div>
      <p>Name: {user.name}</p>
      <p>Email: {user.email}</p>
      <button onClick={updateName}>Change Name</button>
      <button onClick={updateEmail}>Change Email</button>
    </div>
  );
}

Always spread the previous object ({...user}) to avoid losing other properties.


Updating State Based on Previous State

Sometimes you need to update state using its old value (like counters or toggling).
In such cases, pass a function to the setter:

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

setCount(prev => prev + 1); // safe way to increment

This avoids stale state issues when React batches updates.


❌ Common Mistakes with useState

Directly modifying state

user.name = "Wrong"; // ❌ This won’t re-render

Always use setUser.

Forgetting to copy old state (arrays/objects)

setTodos(todos.push("New Task")); // ❌ wrong
setTodos([...todos, "New Task"]); // ✅ correct

Using index as key in lists (only use if no unique ID available).


Summary

  • useState lets us handle dynamic data inside components.
  • We can manage multiple states separately or store arrays/objects.
  • Always use immutability (spread operator, map, filter) when updating arrays/objects.
  • Use functional updates (prev => ...) when depending on old state.

With this deep dive, you can now confidently manage complex state in React.


What’s Next?

In the next blog (Day 10), we’ll learn about useEffect Hook — how to handle side effects like fetching data, timers, and event listeners.

Leave a Reply

Your email address will not be published. Required fields are marked *