← Về danh sách bài họcBài 21/25
🔌 Bài 21: API Integration - Tích Hợp API
🎯 Sau bài học này, bạn sẽ:
- Gọi API với fetch/Axios trong React
- Sử dụng TanStack Query (React Query)
- Xử lý loading, error, retry, cache
- Xây dựng CRUD hoàn chỉnh
1. Fetch API Cơ Bản
// Custom hook pattern
function useFetch(url) {
const [state, setState] = useState({ data: null, loading: true, error: null });
useEffect(() => {
const controller = new AbortController();
setState(prev => ({ ...prev, loading: true }));
fetch(url, { signal: controller.signal })
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.then(data => setState({ data, loading: false, error: null }))
.catch(err => {
if (err.name !== 'AbortError')
setState({ data: null, loading: false, error: err.message });
});
return () => controller.abort();
}, [url]);
return state;
}
function Posts() {
const { data: posts, loading, error } = useFetch('/api/posts');
if (loading) return <div className="skeleton">Loading...</div>;
if (error) return <div className="error">Error: {error}</div>;
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}2. TanStack Query (React Query)
npm install @tanstack/react-queryimport { QueryClient, QueryClientProvider, useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<Posts />
</QueryClientProvider>
);
}
function Posts() {
const queryClient = useQueryClient();
// GET - tự động cache, refetch, retry
const { data: posts, isLoading, error } = useQuery({
queryKey: ['posts'],
queryFn: () => fetch('/api/posts').then(r => r.json()),
staleTime: 5 * 60 * 1000, // 5 phút
});
// POST - mutation
const createPost = useMutation({
mutationFn: (newPost) => fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newPost)
}).then(r => r.json()),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] });
}
});
if (isLoading) return <p>Loading...</p>;
return (
<div>
<button onClick={() => createPost.mutate({ title: 'New' })}>
{createPost.isPending ? 'Creating...' : 'Add Post'}
</button>
{posts?.map(p => <p key={p.id}>{p.title}</p>)}
</div>
);
}💡 Tại sao TanStack Query?
• Auto caching, background refetching
• Retry on error, optimistic updates
• Window focus refetching, pagination support
• Giảm 90% boilerplate code so với useEffect
• Auto caching, background refetching
• Retry on error, optimistic updates
• Window focus refetching, pagination support
• Giảm 90% boilerplate code so với useEffect
📝 Tóm Tắt
useFetchcustom hook: OK cho app nhỏ- TanStack Query: caching, retry, mutations cho app lớn
- Luôn xử lý: loading, error, empty states
- Dùng
AbortControllerđể hủy request khi cleanup