State with useState

State is data that changes over time — form inputs, toggled menus, counters, fetched data. useState is the hook that makes it possible.

Basic state

JSX
import { useState } from 'react';

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

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

useState(0) returns [count, setCount] — the current value and a function to update it. When setCount is called, React re-renders the component with the new value.

Updating objects

JSX
function Profile() {
  const [user, setUser] = useState({ name: 'Ram', age: 22 });

  const haveBirthday = () => {
    // ❌ Don't mutate directly
    // user.age++;

    // ✅ Spread and override
    setUser({ ...user, age: user.age + 1 });
  };

  return (
    <div>
      <p>{user.name}, age {user.age}</p>
      <button onClick={haveBirthday}>Happy Birthday!</button>
    </div>
  );
}

Updating arrays

JSX
function TodoList() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState('');

  const addTodo = () => {
    if (!input.trim()) return;
    setTodos([...todos, { id: Date.now(), text: input, done: false }]);
    setInput('');
  };

  const toggleTodo = (id) => {
    setTodos(todos.map(todo =>
      todo.id === id ? { ...todo, done: !todo.done } : todo
    ));
  };

  return (
    <div>
      <input value={input} onChange={e => setInput(e.target.value)} />
      <button onClick={addTodo}>Add</button>
      {todos.map(todo => (
        <div key={todo.id} onClick={() => toggleTodo(todo.id)}>
          {todo.done ? '✅' : '⬜'} {todo.text}
        </div>
      ))}
    </div>
  );
}

Derived state

JSX
function Cart({ items }) {
  // Don't store this as state — derive it!
  const total = items.reduce((sum, item) => sum + item.price * item.qty, 0);

  return <p>Total: NPR {total}</p>;
}

If you can compute it from existing state or props, don't store it as separate state. This prevents bugs from forgetting to update derived values.

Tips

  • Never mutate state directly — always use the setter function.
  • State updates are asynchronous — React batches updates for performance.
  • Derive what you can from state/props instead of storing redundant copies.
  • Keep state as close to where it's used as possible.

Next: events.