React State: Managing Local Component State with useState Hook

While Props allow parent components to pass data downward, State is the internal memory of a component. State allows React components to remember information between user interactions—such as text entered into an input box, the current page number of a paginated table, or whether a dropdown menu is open.

Introducing the useState Hook

In modern React, local state is declared using the built-in useState hook. You import it directly from the react package:

import React, { useState } from 'react';

function SimpleCounter() {
  // Array Destructuring: [currentValue, setterFunction] = useState(initialValue)
  const [count, setCount] = useState(0);

  return (
    <div className="counter-card">
      <h3>Current Count: {count}</h3>
      <button onClick={() => setCount(count + 1)}>
        Increment (+1)
      </button>
      <button onClick={() => setCount(0)} style={{ marginLeft: '10px' }}>
        Reset
      </button>
    </div>
  );
}

export default SimpleCounter;

Anatomy of useState:

  • useState(0): Initializes the state variable with 0 on the component's initial mount.
  • count: The current state value during this render cycle.
  • setCount: The setter function. Invoking it notifies React to re-render the component with the new value.

Functional State Updates (Preventing Race Conditions)

Because React batches state updates together for performance, calculating next state based on the current state variable can occasionally lead to stale values if triggered repeatedly in a single event handler. Always pass an updater callback when next state depends on prior state:

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

  const handleTripleAdd = () => {
    // ❌ Problematic: count is 0 in all three lines! Results in count = 1
    // setCount(count + 1);
    // setCount(count + 1);
    // setCount(count + 1);

    // ✅ Clean & Correct: Uses functional updater; Results in count = 3
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
  };

  return <button onClick={handleTripleAdd}>Triple Increment (+3)</button>;
}

Managing Object State (Immutability Rules)

In JavaScript, objects and arrays are reference types. When updating an object in React state, you must not mutate the object directly. Instead, create a new object using the ES6 Spread operator (...):

function UserProfileForm() {
  const [user, setUser] = useState({
    name: 'Rohan',
    city: 'New Delhi',
    role: 'Editor'
  });

  const updateCity = (newCity) => {
    // ❌ WRONG: user.city = newCity; (React will NOT detect change!)
    
    // ✅ CORRECT: Copy existing properties, then overwrite 'city'
    setUser(prevUser => ({
      ...prevUser,
      city: newCity
    }));
  };

  return (
    <div>
      <p>Name: {user.name} | City: {user.city} | Role: {user.role}</p>
      <button onClick={() => updateCity('Bengaluru')}>
        Relocate to Bengaluru
      </button>
    </div>
  );
}

Managing Array State in React

Similarly, avoid mutator methods like push(), pop(), or splice(). Use non-mutating methods like concat(), filter(), map(), or the spread syntax:

function TodoList() {
  const [todos, setTodos] = useState(["Study React", "Build Projects"]);

  // 1. Add item immutably
  const addTodo = (text) => {
    setTodos(prev => [...prev, text]);
  };

  // 2. Remove item immutably
  const removeTodo = (indexToDelete) => {
    setTodos(prev => prev.filter((_, idx) => idx !== indexToDelete));
  };

  return (
    <ul>
      {todos.map((item, idx) => (
        <li key={idx}>
          {item}
          <button onClick={() => removeTodo(idx)}>✕</button>
        </li>
      ))}
    </ul>
  );
}

Next Step: Event Handling & Synthetic Events

Now that your components can maintain internal state, let us learn how to handle user clicks, keystrokes, and form events in React Event Handling & Synthetic Events.

Explore Related React JS Tutorials

Accelerate your React mastery by studying these tightly connected core tutorials:

Event Handling →

Updating state on button clicks and input changes.

useReducer Hook →

Managing complex interdependent state transitions.

Controlled Form Inputs →

Binding input elements directly to React state.

Zustand Global State →

Scaling state beyond single components cleanly.

Share Share on Facebook Share on Twitter Share on LinkedIn Pin on Pinterest Share on Stumbleupon Share on Tumblr Share on Reddit Share on Diggit

You may also like this!