Loading و Error States في React: التعامل مع التحميل والأخطاء
تخيّل أن المستخدم فتح صفحة بيانات، والواجهة بقيت فارغة لثوانٍ بدون أي مؤشر. أو ظهر خطأ مفاجئ برسالة غير مفهومة. النتيجة؟ فقدان الثقة. لهذا السبب، حالات التحميل والأخطاء ليست تفاصيل — هي جزء أساسي من تجربة المستخدم.
لماذا يجب أن تهتم بـ Loading & Error States في React؟
- تمنح المستخدم إشعارًا أن التطبيق يعمل ولا يتجمّد.
- تقلّل الإحباط عند حدوث فشل في الشبكة أو السيرفر.
- تجعل الواجهة احترافية وسهلة الاستخدام.
الفكرة الأساسية: ثلاث حالات لأي طلب بيانات
أي طلب بيانات يمر بثلاث حالات طبيعية:
- Loading: الطلب جارٍ التنفيذ.
- Success: البيانات وصلت وتُعرض.
- Error: حدث فشل ويجب إظهار رسالة مناسبة.
مثال عملي: جلب بيانات مع Loading و Error
import { useEffect, useState } from "react";
function Users() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => {
if (!res.ok) throw new Error("فشل تحميل البيانات");
return res.json();
})
.then((data) => setUsers(data))
.catch((err) => setError(err.message))
.finally(() => setLoading(false));
}, []);
if (loading) return <p>جاري التحميل...</p>;
if (error) return <p className="text-danger">{error}</p>;
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
هذا المثال يُظهر ثلاث حالات واضحة للمستخدم. النتيجة: تجربة مفهومة حتى عند فشل الطلب.
شرح ما يحدث خطوة بخطوة:
نبدأ بـ loading = true لأن الطلب لم ينتهِ بعد.
إذا نجح الطلب نملأ users، وإذا فشل نحفظ رسالة الخطأ في error.
في النهاية نُوقف التحميل عبر setLoading(false) مهما كانت النتيجة.
لاحظ ترتيب الشروط:
أولًا نتحقق من loading ثم error،
وبعدها نعرض البيانات. هذا يمنع عرض قائمة فارغة أو رسالة غير مناسبة.
أفضل الممارسات
- استخدم نصًا بسيطًا ومطمئنًا مثل "جاري التحميل" بدل رسائل تقنية.
- أضف زر "إعادة المحاولة" عند حدوث خطأ.
- لا تُظهر بيانات قديمة بدون تنبيه المستخدم.
تحسين التجربة: إضافة زر إعادة المحاولة
function Users() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [users, setUsers] = useState([]);
async function fetchUsers() {
setLoading(true);
setError("");
try {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
if (!res.ok) throw new Error("فشل تحميل البيانات");
const data = await res.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
useEffect(() => {
fetchUsers();
}, []);
if (loading) return <p>جاري التحميل...</p>;
if (error) {
return (
<div>
<p className="text-danger">{error}</p>
<button onClick={fetchUsers}>إعادة المحاولة</button>
</div>
);
}
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
هنا فصلنا منطق الجلب داخل دالة fetchUsers لكي نعيد استخدامها عند الضغط على زر
"إعادة المحاولة". بهذه الطريقة يحصل المستخدم على تحكم مباشر دون إعادة تحميل الصفحة.
الأسئلة الشائعة — FAQ
كيف أتعامل مع حالة التحميل في React؟
عبر state مثل loading، تُظهر مؤشر تحميل وتُخفي المحتوى حتى انتهاء الطلب.
ما أفضل طريقة لعرض أخطاء API للمستخدم؟
برسالة قصيرة غير تقنية مع زر إعادة المحاولة إن أمكن.
متى أستخدم finally في الطلبات؟
عندما تريد إيقاف حالة التحميل بغض النظر عن نجاح الطلب أو فشله.
كيف أميّز بين خطأ الشبكة وخطأ السيرفر؟
افحص res.ok وأكواد الحالة. إذا فشل الطلب قبل الاستجابة فهذا غالبًا خطأ شبكة.
هل يمكن إظهار Skeleton بدل نص التحميل؟
نعم، ويمكن أن يحسّن التجربة، لكن النص البسيط مناسب للتعلّم والبداية.