useEffect hook
Component дэлгэцэнд гарсны дараа юм хийх шаардлага байнга гардаг — API-аас өгөгдөл татах, хуудасны title өөрчлөх, цаг тоолуур эхлүүлэх, хөтчийн event-г сонсох гэх мэт. Эдгээр нь side effect буюу рендерийн гадуурх үйлдлүүд юм. useEffect hook нь эдгээр side effect-уудыг зохицуулдаг React-н чухал хэрэгсэл юм.
useEffect үндэс
useEffect нь хоёр аргумент авдаг: ажиллуулах функц, хамааралт (dependency) массив:
import { useState, useEffect } from "react";
function PageTitle({ title }) {
useEffect(() => {
// Component рендер болсны дараа ажиллана
document.title = title;
});
return <h1>{title}</h1>;
}
Dependency массивгүй бол useEffect рендер хийх болгонд ажиллана. Энэ ихэнх тохиолдолд хэт олон удаа ажиллах тул dependency массив нэмэх нь зөв.
Dependency массив
Dependency массив нь useEffect хэзээ ажиллахыг тодорхойлдог:
import { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState("");
// 1. Зөвхөн нэг удаа — component эхлэх үед (mount)
useEffect(() => {
console.log("Component эхэллээ");
}, []);
// 2. count өөрчлөгдөх бүрт
useEffect(() => {
console.log("count өөрчлөгдлөө:", count);
}, [count]);
// 3. count эсвэл name өөрчлөгдөх бүрт
useEffect(() => {
console.log("count эсвэл name өөрчлөгдлөө");
}, [count, name]);
return (
<div>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
<input value={name} onChange={(e) => setName(e.target.value)} />
</div>
);
}
[] хоосон массив нь "нэг л удаа ажилла, өөрчлөлтийг хяр" гэсэн утгатай. [count] гэвэл count өөрчлөгдөх бүрт ажиллана.
API-аас өгөгдөл татах
useEffect-н хамгийн нийтлэг хэрэглээ бол өгөгдөл татах явдал юм:
import { useState, useEffect } from "react";
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchUser() {
try {
setIsLoading(true);
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error("Өгөгдөл татаж чадсангүй");
}
const data = await response.json();
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
}
fetchUser();
}, [userId]); // userId өөрчлөгдөх бүрт дахин татна
if (isLoading) return <p>Ачааллаж байна...</p>;
if (error) return <p>Алдаа: {error}</p>;
if (!user) return null;
return (
<div>
<h2>{user.username}</h2>
<p>{user.xp} XP</p>
</div>
);
}
useEffect дотор async/await шууд бичиж болохгүй — тул дотор нь async функц тодорхойлж дуудна.
Cleanup функц
Зарим effect зогсоох хэрэгтэй — цаг тоолуур, event listener зэрэг. useEffect-аас функц буцааснаар cleanup хийнэ:
import { useState, useEffect } from "react";
function LiveClock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
// Interval эхлүүлнэ
const interval = setInterval(() => {
setTime(new Date());
}, 1000);
// Cleanup — component устах үед interval зогсооно
return () => {
clearInterval(interval);
};
}, []); // Нэг удаа л эхлүүлнэ
return <p>{time.toLocaleTimeString("mn-MN")}</p>;
}
function WindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
function handleResize() {
setWidth(window.innerWidth);
}
window.addEventListener("resize", handleResize);
// Cleanup — event listener-г устгана
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return <p>Цонхны өргөн: {width}px</p>;
}
Cleanup нь component дэлгэцнээс гарах (unmount) үед ажиллана. Цэвэрлэгээгүй бол memory leak буюу санах ойн алдагдал үүсэж болно.
Хуудасны title шинэчлэх
import { useEffect } from "react";
function LessonPage({ lessonTitle }) {
useEffect(() => {
document.title = `${lessonTitle} — ulaanbaatar.app`;
// Хуудаснаас гарах үед анхны title-г сэргээнэ
return () => {
document.title = "ulaanbaatar.app";
};
}, [lessonTitle]);
return <h1>{lessonTitle}</h1>;
}
Дараагийн хичээлд:
useEffect cleanup-г гүнзгийрч судална. Хаана cleanup шаардлагатай, хаана шаардлагагүй, алдаанаас хэрхэн зайлсхийх аргуудыг практик жишээгээр ойлгоно.