Custom Hook
React-н built-in hook-ууд (useState, useEffect, useRef гэх мэт) маш хүчирхэг — гэвч заримдаа нэг логикийг олон component-д давтан бичих хэрэгтэй болдог. Custom Hook бол тэрхүү давтагдах логикийг нэг дахин ашиглагдах функцэд хураадаг арга юм. use үгээр эхэлдэг энгийн функц — React-н дүрмийг дагаж чадаад байвал та өөрийн hook бичиж болно.
Custom Hook гэж юу вэ?
Custom Hook бол use үгээр эхэлдэг, бусад hook-уудыг дотроо ашиглаж болдог функц юм. Component биш — JSX буцаадаггүй, зөвхөн утга, функц буцаана:
// hooks/useCounter.js
import { useState } from "react";
function useCounter(initialValue = 0, step = 1) {
const [count, setCount] = useState(initialValue);
function increment() {
setCount((c) => c + step);
}
function decrement() {
setCount((c) => c - step);
}
function reset() {
setCount(initialValue);
}
return { count, increment, decrement, reset };
}
export default useCounter;
// Дурын component-д ашиглана
import useCounter from "./hooks/useCounter";
function ScoreBoard() {
const score = useCounter(0, 10);
const lives = useCounter(3, 1);
return (
<div>
<p>Оноо: {score.count}</p>
<button onClick={score.increment}>+10</button>
<button onClick={score.reset}>Дахилт</button>
<p>Амь: {lives.count}</p>
<button onClick={lives.decrement}>-1</button>
</div>
);
}
useFetch — өгөгдөл татах hook
API-аас өгөгдөл татах логик олон component-д давтагддаг — custom hook болгоход хамгийн тохиромжтой тохиолдол:
// hooks/useFetch.js
import { useState, useEffect } from "react";
function useFetch(url) {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return;
let isCancelled = false;
async function fetchData() {
try {
setIsLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP алдаа: ${response.status}`);
const json = await response.json();
if (!isCancelled) setData(json);
} catch (err) {
if (!isCancelled) setError(err.message);
} finally {
if (!isCancelled) setIsLoading(false);
}
}
fetchData();
return () => {
isCancelled = true;
};
}, [url]);
return { data, isLoading, error };
}
export default useFetch;
// Хэрэглэх — логик нуугдаж, component цэвэр болно
import useFetch from "./hooks/useFetch";
function CourseList() {
const { data: courses, isLoading, error } = useFetch("/api/courses");
if (isLoading) return <p>Ачааллаж байна...</p>;
if (error) return <p>Алдаа: {error}</p>;
return (
<ul>
{courses.map((c) => (
<li key={c.id}>{c.title}</li>
))}
</ul>
);
}
useLocalStorage — тогтвортой хадгалах
Хөтчийн localStorage-тай ажиллах логикийг hook болгосон жишээ:
// hooks/useLocalStorage.js
import { useState } from "react";
function useLocalStorage(key, initialValue) {
const [stored, setStored] = useState(() => {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
function setValue(value) {
try {
const valueToStore = value instanceof Function ? value(stored) : value;
setStored(valueToStore);
localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (err) {
console.error("localStorage алдаа:", err);
}
}
return [stored, setValue];
}
export default useLocalStorage;
import useLocalStorage from "./hooks/useLocalStorage";
function ThemeToggle() {
const [theme, setTheme] = useLocalStorage("theme", "dark");
return (
<button onClick={() => setTheme((t) => (t === "dark" ? "light" : "dark"))}>
{theme === "dark" ? "☀️ Цайвар" : "🌙 Харанхуй"}
</button>
);
}
useDebounce — хайлтыг оновчлох
Хэрэглэгч бичиж байхад API-д хэт олон хүсэлт илгээхээс сэргийлэх debounce hook:
// hooks/useDebounce.js
import { useState, useEffect } from "react";
function useDebounce(value, delay = 300) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
export default useDebounce;
import { useState } from "react";
import useDebounce from "./hooks/useDebounce";
import useFetch from "./hooks/useFetch";
function SearchPage() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 400);
const { data, isLoading } = useFetch(
debouncedQuery ? `/api/search?q=${debouncedQuery}` : null,
);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Хайх..."
/>
{isLoading && <p>Хайж байна...</p>}
{data?.map((item) => (
<p key={item.id}>{item.title}</p>
))}
</div>
);
}
Custom hook-уудыг хооронд нь хослуулж болдог — useDebounce + useFetch хамт ажиллаж байгааг анзаарна уу. Энэ бол React-н хамгийн хүчирхэг боломжуудын нэг: жижиг, нэг ажил хийдэг hook-уудыг найруулан том зүйл бүтээх.
Дараагийн хичээлд:
React.memo ашиглан component-г дахин рендер болохоос хамгаалах аргыг судална. Хэзээ memo хэрэгтэй, хэзээ хэрэггүй, буруу хэрэглэлийн нийтлэг алдаануудтай танилцана.