← Về danh sách bài họcBài 5/25
🔄 Bài 5: useState - Quản Lý State
🎯 Sau bài học này, bạn sẽ:
- Hiểu state là gì và tại sao cần state
- Sử dụng useState với các kiểu dữ liệu khác nhau
- Biết functional updates và batching
- Quản lý state với objects và arrays
- Tránh các lỗi phổ biến khi dùng useState
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
useStatetạo state nội bộ cho component- Dùng
prev => ...khi state mới phụ thuộc state cũ - Immutability: không mutate trực tiếp, luôn tạo bản sao mới
- Object:
{...prev, key: value}| Array:[...prev, item] - Lazy init:
useState(() => expensive())cho tính toán nặng