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

🌐 Bài 7: useContext - Chia Sẻ Dữ Liệu Toàn Cục

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

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

1. Vấn Đề Prop Drilling

Prop drilling xảy ra khi bạn phải truyền props qua nhiều tầng component chỉ để đến component con sâu bên trong.

// ❌ Prop drilling: theme phải truyền qua 3 tầng
function App() {
    const [theme, setTheme] = useState('dark');
    return <Layout theme={theme} />;
}
function Layout({ theme }) {
    return <Sidebar theme={theme} />;
}
function Sidebar({ theme }) {
    return <button className={theme}>Menu</button>;
}

// ✅ Context: component nào cũng truy cập được trực tiếp!

2. Tạo và Sử Dụng Context

import { createContext, useContext, useState } from 'react';

// Bước 1: Tạo Context
const ThemeContext = createContext('light');

// Bước 2: Tạo Provider component
function ThemeProvider({ children }) {
    const [theme, setTheme] = useState('light');

    const toggleTheme = () => {
        setTheme(prev => prev === 'light' ? 'dark' : 'light');
    };

    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            {children}
        </ThemeContext.Provider>
    );
}

// Bước 3: Sử dụng useContext
function Header() {
    const { theme, toggleTheme } = useContext(ThemeContext);
    return (
        <header className={theme}>
            <h1>My App</h1>
            <button onClick={toggleTheme}>
                {theme === 'light' ? '🌙' : '☀️'}
            </button>
        </header>
    );
}

// Bước 4: Wrap App với Provider
function App() {
    return (
        <ThemeProvider>
            <Header />
            <MainContent />
        </ThemeProvider>
    );
}

3. Auth Context Thực Tế

const AuthContext = createContext(null);

// Custom hook để dùng auth (clean hơn)
function useAuth() {
    const context = useContext(AuthContext);
    if (!context) {
        throw new Error('useAuth phải dùng trong AuthProvider');
    }
    return context;
}

function AuthProvider({ children }) {
    const [user, setUser] = useState(null);

    const login = async (email, password) => {
        const res = await fetch('/api/login', {
            method: 'POST',
            body: JSON.stringify({ email, password })
        });
        const data = await res.json();
        setUser(data.user);
    };

    const logout = () => setUser(null);

    return (
        <AuthContext.Provider value={{ user, login, logout }}>
            {children}
        </AuthContext.Provider>
    );
}

// Sử dụng
function Navbar() {
    const { user, logout } = useAuth();
    return user
        ? <button onClick={logout}>Đăng xuất ({user.name})</button>
        : <a href="/login">Đăng nhập</a>;
}
💡 Best Practice: Tạo custom hook (useAuth, useTheme) wrap useContext + validation. Giúp code gọn hơn và báo lỗi rõ ràng khi dùng sai.

📝 Tóm Tắt