State is data that changes over time and causes components to re-render when updated.

useState Hook

  import { useState } from 'react';

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

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

Functional Updates

When new state depends on previous state, use a function:

  // Can be stale in rapid clicks
setCount(count + 1);

// Always uses latest state
setCount(prev => prev + 1);
setCount(prev => prev + 1); // Correctly adds 2
  

State with Objects

Never mutate state directly — create a new object:

  function ProfileForm() {
    const [user, setUser] = useState({ name: '', email: '' });

    const handleChange = (e) => {
        const { name, value } = e.target;
        setUser(prev => ({ ...prev, [name]: value }));
    };

    return (
        <form>
            <input name="name" value={user.name} onChange={handleChange} />
            <input name="email" value={user.email} onChange={handleChange} />
        </form>
    );
}
  

State with Arrays

  function TodoList() {
    const [todos, setTodos] = useState([]);

    const addTodo = (text) => {
        setTodos(prev => [...prev, { id: Date.now(), text, done: false }]);
    };

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

    const removeTodo = (id) => {
        setTodos(prev => prev.filter(todo => todo.id !== id));
    };
}
  

Lifting State Up

Share state between sibling components via a common parent:

  function TemperatureInput({ label, temperature, onChange }) {
    return (
        <div>
            <label>{label}</label>
            <input value={temperature} onChange={e => onChange(e.target.value)} />
        </div>
    );
}

function Calculator() {
    const [celsius, setCelsius] = useState('');
    const fahrenheit = celsius ? (parseFloat(celsius) * 9/5 + 32).toFixed(1) : '';

    return (
        <div>
            <TemperatureInput
                label="Celsius"
                temperature={celsius}
                onChange={setCelsius}
            />
            <p>Fahrenheit: {fahrenheit}°F</p>
        </div>
    );
}
  

Multiple State Variables

Prefer multiple useState calls over one large object:

  const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [loading, setLoading] = useState(false);
  

State Initialization

Lazy initialization for expensive computations:

  const [data, setData] = useState(() => {
    const saved = localStorage.getItem('data');
    return saved ? JSON.parse(saved) : defaultData;
});
  

State belongs to the component that owns the data — lift it up only as high as needed.