← Back to Chapters

React useState() Hook

⚛️ React useState() Hook

Hook State Management Functional Components

? Quick Overview

The useState() Hook lets you add state variables to React functional components. Whenever a state value changes, React automatically re-renders the component and shows the latest UI.

  • Used inside functional components only.
  • Returns a pair: current state value + a function to update it.
  • Works with numbers, strings, booleans, arrays, objects, and more.
  • State updates are asynchronous and may be batched.

? Key Concepts

  • State – Data that changes over time and affects what you see on the screen.
  • State updater function – A function that updates the state and triggers a re-render.
  • Initial value – The starting value for your state variable.
  • Immutable updates – Never mutate arrays/objects directly; always create a copy.
  • Functional updates – Use the previous state value safely inside the updater function.

? Syntax & Theory

The basic syntax of useState() looks like this:

? View Code Example
// Basic useState syntax pattern
const [state, setState] = useState(initialValue);
  • state – Current value of the state variable.
  • setState – Function used to update that value.
  • initialValue – Initial state (number, string, boolean, array, object, etc.).

? Example 1: Basic Counter

A simple counter that increments, decrements, and resets a numeric value using useState().

? View Code Example
// Counter component using useState for a number
import React, { useState } from "react";

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

return (
<div className="text-center">
<h4>Count: {count}</h4>
<button
className="btn btn-primary me-2"
onClick={() => setCount(count + 1)}
>
+
</button>
<button
className="btn btn-danger me-2"
onClick={() => setCount(count - 1)}
>
-
</button>
<button
className="btn btn-secondary"
onClick={() => setCount(0)}
>
Reset
</button>
</div>
);
}

export default Counter;

? What Happens?

  • Click +setCount(count + 1) increases the value.
  • Click -setCount(count - 1) decreases the value.
  • Click ResetsetCount(0) sets the counter back to 0.
  • Each update triggers a re-render, so the latest count is always displayed.

? Example 2: Strings & Booleans

Here, a boolean state controls the greeting message and button style.

? View Code Example
// Toggle login status using a boolean state
import React, { useState } from "react";

function Greeting() {
const [loggedIn, setLoggedIn] = useState(false);

return (
<div className="text-center mt-3">
<h5>{loggedIn ? "Welcome back!" : "Please log in"}</h5>
<button
className={`btn ${loggedIn ? "btn-danger" : "btn-success"}`}
onClick={() => setLoggedIn(!loggedIn)}
>
{loggedIn ? "Logout" : "Login"}
</button>
</div>
);
}

export default Greeting;

? What Happens?

  • If loggedIn is false → user sees “Please log in” and a green Login button.
  • After clicking, loggedIn becomes true → message changes to “Welcome back!” and the button turns red with text Logout.
  • Clicking again toggles the state back and forth.

? Example 3: Managing Objects

Use objects in state when related pieces of data belong together (like user profile info).

? View Code Example
// Profile component storing an object in state
import React, { useState } from "react";

function Profile() {
const [user, setUser] = useState({ name: "John", age: 25 });

const updateAge = () => {
setUser({ ...user, age: user.age + 1 });
};

return (
<div className="text-center">
<h4>{user.name}, {user.age} years old</h4>
<button className="btn btn-info" onClick={updateAge}>
Increase Age
</button>
</div>
);
}

export default Profile;

? What Happens?

  • State starts as {'{ name: "John", age: 25 }'}.
  • Clicking the button calls updateAge(), which creates a new object.
  • {'setUser({ ...user, age: user.age + 1 })'} copies all existing properties and only changes age.

? Example 4: Arrays with useState

When managing lists (like to-do items), store them as arrays in state and always create a new array when updating.

? View Code Example
// Todo list component using an array in state
import React, { useState } from "react";

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

const addTodo = () => {
setTodos([...todos, "Build a project"]);
};

return (
<div>
<ul>
{todos.map((todo, i) => (
<li key={i}>{todo}</li>
))}
</ul>
<button
className="btn btn-outline-primary"
onClick={addTodo}
>
Add Todo
</button>
</div>
);
}

export default TodoList;

? What Happens?

  • Initial list: ["Learn React", "Practice useState"].
  • Each click of Add Todo adds "Build a project" at the end.
  • [...todos, "Build a project"] creates a new array (immutability preserved).

⚙️ Functional Updates

If the new state depends on the previous state value, use the functional form of the updater. This guarantees React uses the most recent value, even when updates are batched.

? View Code Example
// Step counter using functional updates
import React, { useState } from "react";

function StepCounter() {
const [step, setStep] = useState(0);

const increment = () => {
setStep((prev) => prev + 1);
};

return (
<div className="text-center">
<h5>Steps Taken: {step}</h5>
<button className="btn btn-primary" onClick={increment}>
Next Step
</button>
</div>
);
}

export default StepCounter;

? Why Functional Updates?

  • React may batch multiple state updates for performance.
  • Using setStep(step + 1) can sometimes use an outdated step value.
  • setStep((prev) => prev + 1) always receives the latest value, making it safer.

? Common Use Cases for useState()

  • Form inputs (text fields, checkboxes, radio buttons).
  • Showing or hiding UI elements (modals, dropdowns, tooltips).
  • Counters, ratings, and like buttons.
  • Tabs and navigation state.
  • Tracking filters, search text, or sort order.

? Tips & Best Practices

  • Use separate useState calls for logically distinct pieces of data.
  • Prefer functional updates like (prev) => prev + 1 when the new value depends on the old one.
  • Never modify state directly (e.g., state.push() or state.age++); always create a new array/object.
  • Use the spread operator (...user, ...todos) to copy existing data while updating only what changed.
  • Remember: state updates are asynchronous; if you need to react to an update, consider using useEffect().
  • Keep state as minimal as possible; derive values (like lengths, formatted text) when rendering instead.

? Try It Yourself

  1. Build a temperature converter using useState for Celsius and Fahrenheit. Update the other value whenever one changes.
  2. Create a full to-do app that can add, delete, and clear tasks using array state.
  3. Make a login form that shows different buttons and messages based on login status.
  4. Experiment with functional updates by incrementing a counter inside a loop and compare with a non-functional approach.

Goal: Become comfortable declaring, updating, and reading state variables using useState() with numbers, strings, booleans, arrays, and objects.