Context дэвшилтэт хэрэглээ
useContext hook-ийг өмнөх хичээлд үзсэн. Энэ хичээлд Context-ийг бодит аппад хэрхэн зөв зохион байгуулах, useReducer-тэй хослуулах, болон гүйцэтгэлийн асуудлаас зайлсхийх аргуудыг сурна. Энэ мэдлэг том аппуудад маш хэрэгтэй!
Custom hook-оор Context-ийг ариун цэвэр болгох
Context-ийг шууд export хийхийн оронд custom hook-оор боож хэрэглэх нь хамгийн зөв pattern:
import { createContext, useContext, useState } from "react";
// Context-ийг модулийн дотор л нуун хадгална
const AuthContext = createContext(null);
// Provider component — state ба логикийг агуулна
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
function login(email, password) {
// жинхэнэ аппад энд API дуудна
setUser({ email, name: "Болд" });
}
function logout() {
setUser(null);
}
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
// Custom hook — AuthContext-ийг шууд биш, энээр дуудна
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) {
throw new Error("useAuth нь AuthProvider дотор хэрэглэгдэх ёстой");
}
return ctx;
}
Аппдаа ийнхүү хэрэглэнэ:
import { AuthProvider, useAuth } from "./context/AuthContext";
// Дээд түвшинд Provider-г боодог
function App() {
return (
<AuthProvider>
<Navbar />
<Main />
</AuthProvider>
);
}
// Хаана ч ашиглаж болно
function Navbar() {
const { user, logout } = useAuth();
return (
<nav>
{user ? (
<>
<span>Сайн уу, {user.name}!</span>
<button onClick={logout}>Гарах</button>
</>
) : (
<a href="/login">Нэвтрэх</a>
)}
</nav>
);
}
useReducer-тэй хослуулах
Нарийн төвөгтэй state-ийг Context + useReducer хослолоор зохицуулах нь цэвэр, хянахад хялбар:
import { createContext, useContext, useReducer } from "react";
// State-ийн бүтэц
const initialState = {
items: [],
total: 0,
};
// Reducer — state-ийг хэрхэн өөрчлөх тодорхойлно
function cartReducer(state, action) {
switch (action.type) {
case "ADD_ITEM": {
const exists = state.items.find((i) => i.id === action.item.id);
const items = exists
? state.items.map((i) =>
i.id === action.item.id ? { ...i, qty: i.qty + 1 } : i,
)
: [...state.items, { ...action.item, qty: 1 }];
return { items, total: items.reduce((s, i) => s + i.price * i.qty, 0) };
}
case "REMOVE_ITEM": {
const items = state.items.filter((i) => i.id !== action.id);
return { items, total: items.reduce((s, i) => s + i.price * i.qty, 0) };
}
case "CLEAR":
return initialState;
default:
return state;
}
}
const CartContext = createContext(null);
export function CartProvider({ children }) {
const [state, dispatch] = useReducer(cartReducer, initialState);
// Dispatch-ийг шууд дамжуулах оронд helper функцүүд гаргана
function addItem(item) {
dispatch({ type: "ADD_ITEM", item });
}
function removeItem(id) {
dispatch({ type: "REMOVE_ITEM", id });
}
function clearCart() {
dispatch({ type: "CLEAR" });
}
return (
<CartContext.Provider value={{ ...state, addItem, removeItem, clearCart }}>
{children}
</CartContext.Provider>
);
}
export function useCart() {
const ctx = useContext(CartContext);
if (!ctx) throw new Error("useCart нь CartProvider дотор хэрэглэгдэх ёстой");
return ctx;
}
Олон Context хослуулах
Нэг том Context-д бүгдийг хийхийн оронд сэдвээр нь ангилж тусдаа Context үүсгэх нь дээр:
// app/providers.jsx — бүх Provider-уудыг нэг газарт цуглуулна
import { AuthProvider } from "./context/AuthContext";
import { CartProvider } from "./context/CartContext";
import { ThemeProvider } from "./context/ThemeContext";
export function AppProviders({ children }) {
return (
<AuthProvider>
<ThemeProvider>
<CartProvider>{children}</CartProvider>
</ThemeProvider>
</AuthProvider>
);
}
// main.jsx
import { AppProviders } from "./app/providers";
createRoot(document.getElementById("root")).render(
<AppProviders>
<App />
</AppProviders>,
);
Context-н гүйцэтгэлийн нюанс
Context value өөрчлөгдөх болгонд тухайн Context-г subscribe хийсэн бүх component дахин рендерлэгдэнэ. Тиймээс value-г тогтворжуулах нь чухал:
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("dark");
// ❌ Муу — рендерлэгдэх болгонд шинэ object үүснэ
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("dark");
// ✅ Дээр — useMemo-гоор value-г тогтворжуулна
const value = useMemo(() => ({ theme, setTheme }), [theme]);
return (
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
);
}
Context нь дунд хэмжээний глобал state-д тохиромжтой. Маш олон газарт өөрчлөгдөх state-д Zustand зэрэг тусгай library ашиглах нь дээр — 31-р хичээлд үүнийг сурна.
Дараагийн хичээлд:
React Router сурна — React аппд хэд хэдэн хуудас (route) хэрхэн үүсгэхийг ойлгоно. BrowserRouter, Routes, Route, Link зэрэг үндсэн component-уудыг танина.