Өгөгдөл татах (fetch + useEffect)
Бодит апп нь серверээс өгөгдөл татдаг — хэрэглэгчийн профайл, нийтлэлийн жагсаалт, бүтээгдэхүүний мэдээлэл гэх мэт. React-д өгөгдөл татахад fetch API ба useEffect hook хамтдаа ажилладаг. Энэ pattern-ийг сайн эзэмшвэл аль ч аппын backend-тэй ажиллаж чаддаг болно!
Үндсэн pattern: fetch + useEffect
import { useState, useEffect } from "react";
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchUsers() {
try {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
if (!res.ok) {
throw new Error(`Алдаа: ${res.status}`);
}
const data = await res.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
// Амжилттай эсвэл алдаатай байсан ч loading дуусна
setLoading(false);
}
}
fetchUsers();
}, []); // Зөвхөн mount-д нэг удаа татна
if (loading) return <p>Ачааллаж байна...</p>;
if (error) return <p style={{ color: "red" }}>Алдаа: {error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} — {user.email}
</li>
))}
</ul>
);
}
Энэ pattern гурван төлөвтэй: loading (татаж байна), error (алдаа гарлаа), data (амжилттай ирлээ). Гурвыг зохион байгуулах нь мэргэжлийн апп хийхэд зайлшгүй.
Params-аас хамааралтай таталт
URL parameter өөрчлөгдөх болгонд шинэ өгөгдөл татах хэрэгтэй болдог:
import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
function PostDetail() {
const { postId } = useParams();
const [post, setPost] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// postId өөрчлөгдөх болгонд дахин татна
let cancelled = false; // race condition-оос сэргийлнэ
async function fetchPost() {
setLoading(true);
setError(null);
try {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`,
);
if (!res.ok) throw new Error("Нийтлэл олдсонгүй");
const data = await res.json();
// Component unmount болсон бол state тавихгүй
if (!cancelled) setPost(data);
} catch (err) {
if (!cancelled) setError(err.message);
} finally {
if (!cancelled) setLoading(false);
}
}
fetchPost();
// Cleanup: дараагийн effect ажиллахын өмнө cancelled болгоно
return () => {
cancelled = true;
};
}, [postId]); // postId dependency-д байх ёстой
if (loading) return <p>Нийтлэл ачааллаж байна...</p>;
if (error) return <p style={{ color: "red" }}>{error}</p>;
return (
<article>
<h1>{post?.title}</h1>
<p>{post?.body}</p>
</article>
);
}
cancelled flag нь race condition буюу хуучин хүсэлтийн хариу шинийнхийг дарах асуудлаас хамгаалдаг. Хэрэглэгч хурдан дараалан postId солиход энэ нөхцөл үүсч болдог.
Custom hook болгон гаргах
Өгөгдөл татах логикийг custom hook болгож хэд хэдэн газарт дахин ашиглаж болно:
import { useState, useEffect } from "react";
// Дахин ашиглагдах fetch hook
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return;
let cancelled = false;
setLoading(true);
setError(null);
async function fetchData() {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP алдаа: ${res.status}`);
const json = await res.json();
if (!cancelled) setData(json);
} catch (err) {
if (!cancelled) setError(err.message);
} finally {
if (!cancelled) setLoading(false);
}
}
fetchData();
return () => {
cancelled = true;
};
}, [url]);
return { data, loading, error };
}
// Хэрэглэх нь маш хялбар болно
function AlbumList() {
const {
data: albums,
loading,
error,
} = useFetch("https://jsonplaceholder.typicode.com/albums");
if (loading) return <p>Ачааллаж байна...</p>;
if (error) return <p style={{ color: "red" }}>{error}</p>;
return (
<ul>
{albums.map((album) => (
<li key={album.id}>{album.title}</li>
))}
</ul>
);
}
function UserProfile({ userId }) {
const {
data: user,
loading,
error,
} = useFetch(
userId ? `https://jsonplaceholder.typicode.com/users/${userId}` : null,
);
if (loading) return <p>Профайл ачааллаж байна...</p>;
if (error) return <p style={{ color: "red" }}>{error}</p>;
return <h2>{user?.name}</h2>;
}
POST, PUT, DELETE хүсэлтүүд
Зөвхөн өгөгдөл татахаас гадна өгөгдөл илгээх, өөрчлөх, устгах:
import { useState } from "react";
function CreatePost() {
const [title, setTitle] = useState("");
const [submitting, setSubmitting] = useState(false);
const [result, setResult] = useState(null);
async function handleSubmit(e) {
e.preventDefault();
setSubmitting(true);
try {
const res = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title, userId: 1 }),
});
const data = await res.json();
setResult(data);
setTitle("");
} catch (err) {
console.error("Алдаа:", err);
} finally {
setSubmitting(false);
}
}
return (
<form onSubmit={handleSubmit}>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Нийтлэлийн гарчиг"
/>
<button type="submit" disabled={submitting}>
{submitting ? "Илгээж байна..." : "Нийтлэх"}
</button>
{result && <p>Амжилттай! ID: {result.id}</p>}
</form>
);
}
Энэ хичээлд үзсэн pattern-ууд сайн боловч жинхэнэ аппад их давтагдах код үүснэ. Дараагийн хичээлд TanStack Query ашиглан энэ бүхнийг хэд дахин хялбар болгоно!
Дараагийн хичээлд:
TanStack Query (хуучнаар React Query) сурна — өгөгдөл татах, cache хийх, автоматаар дахин татах зэрэг бүх зүйлийг автоматчилсан хүчирхэг library. fetch + useEffect pattern-г орлуулдаг орчин үеийн стандарт.