← Về danh sách bài họcBài 5/25

🔄 Bài 5: useState - Quản Lý State

⏱️ Thời gian đọc: 15 phút | 📚 Độ khó: Trung bình

🎯 Sau bài học này, bạn sẽ:

1. State Là Gì?

State là dữ liệu nội bộ của component, khi state thay đổi thì component tự động re-render. Khác với props (từ bên ngoài), state do component tự quản lý.

import { useState } from 'react';

function Counter() {
    // Khai báo state: [giáTrị, hàmCậpNhật] = useState(giáTrịBanĐầu)
    const [count, setCount] = useState(0);

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

2. Functional Updates

Khi state mới phụ thuộc vào state cũ, dùng callback form:

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

    // ❌ Sai: 3 lần gọi nhưng count chỉ +1 (vì dùng cùng giá trị count)
    const addThreeBad = () => {
        setCount(count + 1);
        setCount(count + 1);
        setCount(count + 1); // count vẫn chỉ +1
    };

    // ✅ Đúng: dùng callback, count +3
    const addThreeGood = () => {
        setCount(prev => prev + 1);
        setCount(prev => prev + 1);
        setCount(prev => prev + 1); // count +3
    };

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={addThreeGood}>+3</button>
        </div>
    );
}
⚠️ Quy tắc quan trọng: Luôn dùng prev => ... khi state mới phụ thuộc state cũ. Đặc biệt quan trọng trong setTimeout, setInterval, event handlers.

3. State Với Object và Array

function UserForm() {
    const [user, setUser] = useState({
        name: '',
        email: '',
        age: 0
    });
    const [hobbies, setHobbies] = useState(['Đọc sách']);

    // ✅ Update object: PHẢI spread toàn bộ
    const updateName = (name) => {
        setUser(prev => ({ ...prev, name }));
    };

    // ❌ Sai: mutate trực tiếp
    // user.name = 'An'; // KHÔNG BAO GIỜ làm thế này!

    // ✅ Thêm item vào array
    const addHobby = (hobby) => {
        setHobbies(prev => [...prev, hobby]);
    };

    // ✅ Xóa item khỏi array
    const removeHobby = (index) => {
        setHobbies(prev => prev.filter((_, i) => i !== index));
    };

    // ✅ Update item trong array
    const updateHobby = (index, newValue) => {
        setHobbies(prev => prev.map((h, i) =>
            i === index ? newValue : h
        ));
    };

    return (
        <div>
            <input
                value={user.name}
                onChange={(e) => updateName(e.target.value)}
                placeholder="Tên"
            />
            <ul>
                {hobbies.map((h, i) => (
                    <li key={i}>
                        {h}
                        <button onClick={() => removeHobby(i)}>Xóa</button>
                    </li>
                ))}
            </ul>
        </div>
    );
}
💡 Nguyên tắc Immutability: Trong React, KHÔNG BAO GIỜ mutate state trực tiếp. Luôn tạo bản sao mới bằng spread operator (...) hoặc các method như map, filter, concat.

4. Lazy Initialization

// ❌ Hàm computeExpensive() được gọi MỖI lần render
const [data, setData] = useState(computeExpensive());

// ✅ Hàm chỉ gọi 1 LẦN khi mount (truyền function, không gọi)
const [data, setData] = useState(() => computeExpensive());

// Ví dụ thực tế: đọc localStorage
const [theme, setTheme] = useState(() => {
    return localStorage.getItem('theme') || 'light';
});

📝 Tóm Tắt