Thư viện JavaScript phổ biến nhất để xây dựng giao diện người dùng - Hooks, Components, State Management
React là thư viện JavaScript được phát triển bởi Facebook (Meta) từ năm 2013. React cho phép xây dựng giao diện người dùng (UI) theo mô hình component-based, giúp code dễ quản lý và tái sử dụng.
# Tạo project mới với Vite (khuyên dùng)
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev
# Hoặc dùng Create React App
npx create-react-app my-app
cd my-app
npm start
import { useState } from 'react';
function App() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Xin chào, React! ⚛️</h1>
<p>Bạn đã nhấn {count} lần</p>
<button onClick={() => setCount(count + 1)}>
Nhấn vào đây
</button>
</div>
);
}
export default App;
Giải thích:
• useState(0): Tạo state với giá trị ban đầu là 0
• setCount: Hàm cập nhật state
• JSX: Cú pháp HTML trong JavaScript
📌 Bài 1: Giới thiệu React & Cài đặt
✨ Bài 2: JSX - Cú pháp trong React
🧩 Bài 3: Components - Thành phần giao diện
📦 Bài 4: Props - Truyền dữ liệu giữa Components
🔄 Bài 5: useState - Quản lý State
⚡ Bài 6: useEffect - Side Effects
🌐 Bài 7: useContext - Chia sẻ dữ liệu toàn cục
📍 Bài 8: useRef - Tham chiếu DOM & giá trị persistent
🧠 Bài 9: useMemo - Tối ưu tính toán
🔗 Bài 10: useCallback - Tối ưu hàm callback
🎛️ Bài 11: useReducer - Quản lý state phức tạp
📐 Bài 12: useLayoutEffect - Đo lường và thao tác DOM
🎨 Bài 13: useInsertionEffect - CSS-in-JS & Inject Styles
⏳ Bài 14: useTransition & useDeferredValue
🆔 Bài 15: useId & useSyncExternalStore
🪝 Bài 16: Custom Hooks - Tạo Hooks riêng
🖱️ Bài 17: Event Handling & Forms
📋 Bài 18: Conditional Rendering & Lists
🗺️ Bài 19: React Router - Điều hướng trang
🏪 Bài 20: State Management (Context, Redux, Zustand)
🔌 Bài 21: Tích hợp API & React Query
⚡ Bài 22: Performance Optimization
🧪 Bài 23: Testing React Components
🏗️ Bài 24: React Design Patterns
🚀 Bài 25: Dự án Todo App (Final Project)
Hooks cho phép bạn sử dụng state và các tính năng React khác mà không cần viết class component. Được giới thiệu từ React 16.8.
Hook cơ bản nhất, cho phép component function có state riêng.
import { useState } from 'react';
function Counter() {
// State đơn giản
const [count, setCount] = useState(0);
// State với object
const [user, setUser] = useState({
name: 'An',
age: 25
});
// Functional update (khi state mới phụ thuộc state cũ)
const increment = () => {
setCount(prev => prev + 1);
};
// Update object (phải spread toàn bộ)
const updateName = (newName) => {
setUser(prev => ({ ...prev, name: newName }));
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
<p>User: {user.name}, {user.age} tuổi</p>
</div>
);
}
Xử lý side effects: gọi API, subscriptions, DOM manipulation.
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Side effect: fetch data
const fetchUser = async () => {
setLoading(true);
const res = await fetch(`/api/users/${userId}`);
const data = await res.json();
setUser(data);
setLoading(false);
};
fetchUser();
// Cleanup function (chạy khi unmount hoặc dependency thay đổi)
return () => {
console.log('Cleanup!');
};
}, [userId]); // Dependency array - chỉ re-run khi userId thay đổi
if (loading) return <p>Đang tải...</p>;
return <h2>{user?.name}</h2>;
}
[]: Chạy 1 lần khi mount[a, b]: Chạy khi a hoặc b thay đổi
useLayoutEffect chạy đồng bộ sau khi DOM cập nhật nhưng
trước khi browser paint. Dùng khi cần đo lường DOM hoặc ngăn flash.
import { useLayoutEffect, useRef, useState } from 'react';
function Tooltip({ text, children }) {
const ref = useRef(null);
const [position, setPosition] = useState({ top: 0, left: 0 });
useLayoutEffect(() => {
// Đo DOM TRƯỚC KHI browser paint
// → Không bị "nhấp nháy" (flash)
const rect = ref.current.getBoundingClientRect();
setPosition({
top: rect.top - 30,
left: rect.left + rect.width / 2
});
}, [text]);
return (
<div ref={ref}>
{children}
<span style={{ position: 'fixed', ...position }}>
{text}
</span>
</div>
);
}
// ⚠️ useEffect: render → paint → effect (có thể thấy flash)
// ✅ useLayoutEffect: render → effect → paint (không flash)
• Chỉ gọi Hooks ở top level (không trong if/for/nested functions)
• Chỉ gọi trong React function components hoặc custom hooks
• Đặt tên custom hooks bắt đầu bằng use (VD: useFetch)
• Luôn khai báo đầy đủ dependency array
import { useState, useEffect } from 'react';
// Custom hook tái sử dụng cho fetch data
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
setLoading(true);
const res = await fetch(url, {
signal: controller.signal
});
if (!res.ok) throw new Error('Fetch failed');
const json = await res.json();
setData(json);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
} finally {
setLoading(false);
}
};
fetchData();
// Cleanup: hủy request nếu component unmount
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// Sử dụng
function UserList() {
const { data, loading, error } = useFetch('/api/users');
if (loading) return <p>Đang tải...</p>;
if (error) return <p>Lỗi: {error}</p>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
import { useReducer, useCallback } from 'react';
// Reducer pattern cho state phức tạp
const initialState = { items: [], filter: 'all', loading: false };
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
items: [...state.items, {
id: Date.now(),
text: action.payload,
completed: false
}]
};
case 'TOGGLE_TODO':
return {
...state,
items: state.items.map(item =>
item.id === action.payload
? { ...item, completed: !item.completed }
: item
)
};
case 'SET_FILTER':
return { ...state, filter: action.payload };
default:
return state;
}
}
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, initialState);
const addTodo = useCallback((text) => {
dispatch({ type: 'ADD_TODO', payload: text });
}, []);
const filteredItems = state.items.filter(item => {
if (state.filter === 'active') return !item.completed;
if (state.filter === 'completed') return item.completed;
return true;
});
return (
<div>
<h1>Todo App ⚛️</h1>
{/* ... render UI */}
</div>
);
}
• React Official Documentation
• React Hooks API Reference
• Dịch vụ lập trình ứng dụng - Không Gian AI