---
source_hash: "d2c1ee97"
title: "Chapter 1: useState"
weight: 400
---

# Chapter 1: useState

## The Problem

In plain JavaScript, you can modify a variable:

```javascript
let count = 0;
count = count + 1;
```

But in React, this doesn't work:

```javascript
function Counter() {
    let count = 0;

    const increment = () => {
        count = count + 1;  // The variable changes...
        console.log(count); // Displays 1, 2, 3...
    };

    // But the UI is NEVER updated!
    return (
        <div>
            <p>{count}</p>  {/* Always displays 0 */}
            <button onClick={increment}>+1</button>
        </div>
    );
}
```

**Why?** React doesn't know the variable has changed. You must tell it explicitly.

## The Solution: useState

```javascript
import { useState } from 'react';

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

    const increment = () => {
        setCount(count + 1);  // Tells React to re-render
    };

    return (
        <div>
            <p>{count}</p>
            <button onClick={increment}>+1</button>
        </div>
    );
}
```

`useState` returns an array with:

1. **The current value** (`count`)
2. **A function to update it** (`setCount`)

## Syntax

```javascript
const [state, setState] = useState(initialValue);
```

- `state`: the current value
- `setState`: function to update the value
- `initialValue`: initial value (only once on first render)

## Different State Types

### Numbers

```javascript
const [count, setCount] = useState(0);
setCount(count + 1);
setCount(count - 1);
```

### Strings

```javascript
const [name, setName] = useState('');
setName('John');
setName(e.target.value);  // For an input
```

### Booleans

```javascript
const [isOpen, setIsOpen] = useState(false);
setIsOpen(true);
setIsOpen(!isOpen);  // Toggle
```

### Objects

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

// INCORRECT - direct mutation
user.name = 'John';  // Doesn't work!

// CORRECT - new object with spread
setUser({ ...user, name: 'John' });
```

### Arrays

```javascript
const [items, setItems] = useState([]);

// Add an element
setItems([...items, newItem]);

// Remove an element
setItems(items.filter(item => item.id !== idToRemove));

// Update an element
setItems(items.map(item =>
    item.id === idToUpdate ? { ...item, name: 'New name' } : item
));
```

## Functional Update

When the new value depends on the previous one, use the functional form:

```javascript
// POTENTIAL ISSUE with rapid calls
setCount(count + 1);
setCount(count + 1);  // count is always the old value!
// Result: +1 instead of +2

// CORRECT - functional form
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
// Result: +2
```

The functional form guarantees you're working with the latest value.

## Multiple States in a Component

```javascript
function LoginForm() {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState(null);

    // ...
}
```

Or grouped in an object:

```javascript
function LoginForm() {
    const [form, setForm] = useState({
        email: '',
        password: '',
        isLoading: false,
        error: null
    });

    const updateField = (field, value) => {
        setForm(prev => ({ ...prev, [field]: value }));
    };

    // updateField('email', 'john@example.com');
}
```

## Lazy Initialization

If the initial value requires an expensive calculation, pass a function:

```javascript
// PROBLEM - calculation runs on every render
const [data, setData] = useState(expensiveCalculation());

// CORRECT - calculation runs only once
const [data, setData] = useState(() => expensiveCalculation());
```

## Comparison with PHP

| Concept | PHP (session) | React (useState) |
| --- | --- | --- |
| Storage | `$_SESSION['count'] = 0` | `useState(0)` |
| Read | `$_SESSION['count']` | `count` |
| Write | `$_SESSION['count']++` | `setCount(c => c + 1)` |
| Persistence | Server-side | Browser memory |

## Exercises

### Exercise 1: Counter with min/max

Create a counter that:

- Cannot go below 0
- Cannot exceed 10

**Solution:**

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

    const increment = () => {
        setCount(prev => Math.min(prev + 1, 10));
    };

    const decrement = () => {
        setCount(prev => Math.max(prev - 1, 0));
    };

    return (
        <div>
            <button onClick={decrement} disabled={count === 0}>-</button>
            <span>{count}</span>
            <button onClick={increment} disabled={count === 10}>+</button>
        </div>
    );
}
```

### Exercise 2: Todo List

Create a component that:

- Displays a list of todos
- Allows adding a todo
- Allows deleting a todo

**Solution:**

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

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

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

    return (
        <div>
            <input
                value={input}
                onChange={(e) => setInput(e.target.value)}
                onKeyPress={(e) => e.key === 'Enter' && addTodo()}
            />
            <button onClick={addTodo}>Add</button>

            <ul>
                {todos.map(todo => (
                    <li key={todo.id}>
                        {todo.text}
                        <button onClick={() => removeTodo(todo.id)}>×</button>
                    </li>
                ))}
            </ul>
        </div>
    );
}
```

## Key Points to Remember

1. **useState** returns `[value, setValue]`
2. **Never modify** state directly, always use the setter
3. **Spread operator** for objects and arrays: `{...obj}`, `[...arr]`
4. **Functional form**: `setState(prev => ...)` when the new state depends on the old one
5. State is **local** to the component

[<- Back to module](/training/module3-hooks-fondamentaux) | [Next chapter: useEffect ->](/training/module3-hooks-fondamentaux/useeffect)
