React Hooks - useState và useEffect
React Hooks cho phép sử dụng state và lifecycle features trong function components. Hãy cùng tìm hiểu 2 hooks phổ biến nhất!
1. useState - Quản lý State
Hook để thêm state vào function component:
import React, { useState } from 'react';
function Counter() {
// Khai báo state variable
const [count, setCount] = useState(0);
return (
<div>
<p>Bạn đã click {count} lần</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
// Multiple state variables
function UserForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
return (
<form>
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</form>
);
}
2. useState với Objects và Arrays
Cần spread operator để update state object/array:
function UserProfile() {
const [user, setUser] = useState({
name: 'Huy',
age: 22,
city: 'Hanoi'
});
// Update object - phải spread
const updateName = (newName) => {
setUser({
...user,
name: newName
});
};
// Hoặc dùng functional update
const incrementAge = () => {
setUser(prevUser => ({
...prevUser,
age: prevUser.age + 1
}));
};
}
function TodoList() {
const [todos, setTodos] = useState([]);
// Thêm item
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text }]);
};
// Xóa item
const removeTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
}
3. useEffect - Side Effects
Hook để xử lý side effects (API calls, subscriptions, DOM manipulation):
import React, { useState, useEffect } from 'react';
function UserData() {
const [user, setUser] = useState(null);
// Chạy sau mỗi render
useEffect(() => {
console.log('Component rendered');
});
// Chạy chỉ 1 lần khi mount ([] dependency)
useEffect(() => {
fetch('https://api.example.com/user')
.then(res => res.json())
.then(data => setUser(data));
}, []); // Empty array = chỉ chạy lần đầu
// Chạy khi user thay đổi
useEffect(() => {
console.log('User changed:', user);
}, [user]); // Dependency array
return <div>{user?.name}</div>;
}
4. Cleanup Function
Return function trong useEffect để cleanup:
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
// Setup
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
// Cleanup function
return () => {
clearInterval(interval);
};
}, []); // Chỉ setup/cleanup khi mount/unmount
return <div>{seconds} seconds</div>;
}
function WindowSize() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
// Cleanup: remove listener khi unmount
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
}
5. Fetch Data Pattern
Pattern phổ biến để fetch data:
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
setError(null);
} catch (err) {
setError(err.message);
setData(null);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!data) return null;
return <div>{/* Render data */}</div>;
}
6. Common Mistakes
- Quên dependency array: Gây infinite loop
- Missing dependencies: Stale closure problem
- Async trong useEffect: Không được return Promise trực tiếp
// ❌ SAI - Infinite loop
useEffect(() => {
setCount(count + 1);
}); // Không có dependency array
// ❌ SAI - Async function trực tiếp
useEffect(async () => {
const data = await fetchData();
}, []);
// ✅ ĐÚNG - Async bên trong
useEffect(() => {
const loadData = async () => {
const data = await fetchData();
setData(data);
};
loadData();
}, []);
Kết luận
useState và useEffect là 2 hooks cơ bản nhất trong React:
- useState: Quản lý state trong component
- useEffect: Xử lý side effects
- Luôn chú ý dependency array để tránh bugs
- Cleanup khi cần (timers, subscriptions, listeners)
Thành thạo 2 hooks này là nền tảng để học các hooks khác!