في تطبيقات React الحديثة، لا يكفي أن نبني واجهات ثابتة فقط، بل نحتاج غالبًا إلى جلب البيانات من مصادر خارجية مثل API ثم عرضها للمستخدم داخل الصفحة. لذلك تُعد مهارة التعامل مع API من أهم المهارات الأساسية التي يجب على كل مطور React تعلمها.
في هذا المقال سنشرح طريقة جلب البيانات من API في React باستخدام useEffect و useState، مع مثال عملي لجلب قائمة منتجات من API خارجي وعرضها داخل الصفحة، بالإضافة إلى نسخة محسّنة باستخدام TypeScript.
ما فكرة التطبيق؟
فكرة التطبيق بسيطة جدًا. سنقوم بجلب قائمة منتجات من API خارجي، ثم تخزين هذه البيانات داخل حالة في React، وبعد ذلك نعرض المنتجات داخل الصفحة باستخدام الدالة map.
أثناء انتظار وصول البيانات، سنعرض للمستخدم رسالة تحميل بسيطة مثل Loading... حتى يعرف أن البيانات ما زالت قيد الجلب.
الخطوات التي سنقوم بها
- جلب البيانات من API خارجي.
- تخزين البيانات داخل
state. - عرض البيانات باستخدام
map. - إظهار رسالة تحميل أثناء انتظار البيانات.
- توضيح شكل البيانات باستخدام TypeScript.
كود جلب المنتجات من API في React
في البداية سنستخدم كود React عادي بدون TypeScript. هذا المثال يقوم بجلب المنتجات من موقع Fake Store API ثم يعرض اسم المنتج وصورته وسعره داخل الصفحة.
import { useEffect, useState } from 'react';
function CounterFc() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then(response => response.json())
.then((data) => {
setProducts(data);
});
}, []);
return (
<div>
<h2>Products</h2>
{products.length == 0 ? (
<p>Loading...</p>
) : (
products.map((product) => (
<div key={product.id}>
<img src={product.image} width="100" />
<h3>{product.title}</h3>
<p>${product.price}</p>
</div>
))
)}
</div>
);
}
export default CounterFc;شرح الكود خطوة بخطوة
في بداية الكود قمنا باستيراد useEffect و useState من مكتبة React، لأننا نحتاج إلى أداتين أساسيتين: الأولى لتخزين البيانات داخل المكون، والثانية لتنفيذ عملية جلب البيانات عند تحميل المكون لأول مرة.
import { useEffect, useState } from 'react'; بعد ذلك أنشأنا المكون CounterFc، وداخله قمنا بإنشاء حالة باسم products باستخدام useState. القيمة الابتدائية لهذه الحالة هي مصفوفة فارغة، لأن المنتجات لم تصل بعد من API.
const [products, setProducts] = useState([]); المتغير products يحتوي على قائمة المنتجات، أما الدالة setProducts فهي المسؤولة عن تحديث هذه القائمة بعد وصول البيانات من API.
استخدام useEffect لجلب البيانات
بعد تعريف الحالة استخدمنا useEffect، وهي Hook مهمة في React تُستخدم لتنفيذ كود معين في وقت محدد من دورة حياة المكون.
في هذا المثال، نريد تنفيذ طلب جلب البيانات مرة واحدة فقط عند تحميل المكون لأول مرة، لذلك وضعنا كود fetch داخل useEffect.
useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then(response => response.json())
.then((data) => {
setProducts(data);
});
}, []); الدالة fetch ترسل طلبًا إلى API، وبعد وصول الاستجابة نقوم بتحويلها إلى JSON باستخدام:
response.json() بعد ذلك نحصل على البيانات في المتغير data، ثم نخزنها داخل الحالة باستخدام:
setProducts(data); وجود المصفوفة الفارغة [] في نهاية useEffect يعني أن الكود سيتم تنفيذه مرة واحدة فقط عند تحميل المكون، وهذا هو السلوك المناسب غالبًا عند جلب البيانات من API.
عرض البيانات داخل الصفحة
داخل جزء return قمنا أولًا بعرض عنوان بسيط:
<h2>Products</h2>بعد ذلك كتبنا شرطًا يتحقق من طول مصفوفة المنتجات. إذا كانت المصفوفة فارغة، فهذا يعني أن البيانات لم تصل بعد، لذلك نعرض رسالة تحميل:
{products.length == 0 ? <p>Loading...</p> : ...} أما إذا كانت المنتجات موجودة، نستخدم الدالة map للمرور على كل منتج داخل المصفوفة وعرضه داخل الصفحة.
products.map((product) => (
<div key={product.id}>
<img src={product.image} width="100" />
<h3>{product.title}</h3>
<p>${product.price}</p>
</div>
)) كل منتج يتم عرضه داخل عنصر div مستقل. ونستخدم product.id كقيمة للخاصية key حتى تتمكن React من تتبع العناصر بشكل صحيح أثناء التحديث.
لماذا نستخدم key في React؟
عند عرض قائمة عناصر باستخدام map، تحتاج React إلى قيمة فريدة لكل عنصر حتى تستطيع معرفة أي عنصر تم تغييره أو إضافته أو حذفه.
لذلك نستخدم:
key={product.id} وهذه الطريقة أفضل من استخدام رقم الفهرس index، لأن id غالبًا يكون ثابتًا وفريدًا لكل منتج.
كود جلب البيانات من API باستخدام TypeScript
عند استخدام TypeScript مع React، من الأفضل أن نحدد شكل البيانات القادمة من API. هذا يساعدنا على كتابة كود أوضح، ويقلل الأخطاء أثناء التطوير.
في المثال التالي سنقوم بتعريف interface باسم Product توضح شكل المنتج القادم من API.
import { useEffect, useState } from 'react';
interface Product {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
rate: number;
count: number;
};
}
function CounterFc() {
const [products, setProducts] = useState<Product[]>([]);
useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then(response => response.json())
.then((data) => {
setProducts(data);
});
}, []);
return (
<div>
<h2>Products</h2>
{products.length == 0 ? (
<p>Loading...</p>
) : (
products.map((product) => (
<div key={product.id}>
<img src={product.image} width="100" />
<h3>{product.title}</h3>
<p>${product.price}</p>
</div>
))
)}
</div>
);
}
export default CounterFc;شرح interface Product
في نسخة TypeScript قمنا بتعريف نموذج للمنتج باستخدام interface. هذا النموذج يوضح الخصائص التي نتوقع وجودها في كل منتج.
interface Product {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
rate: number;
count: number;
};
} هذا يعني أن كل منتج يحتوي على رقم تعريف id، واسم المنتج title، والسعر price، والوصف description، والتصنيف category، والصورة image، بالإضافة إلى تقييم المنتج داخل الكائن rating.
تحديد نوع state في TypeScript
بدلًا من كتابة:
const [products, setProducts] = useState([]);استخدمنا:
const [products, setProducts] = useState<Product[]>([]); وهذا يعني أن products ستكون مصفوفة تحتوي على عناصر من نوع Product. بهذه الطريقة يفهم TypeScript شكل البيانات، ويعطيك اقتراحات أفضل أثناء الكتابة، كما يساعدك على اكتشاف الأخطاء مبكرًا.
مميزات استخدام TypeScript مع API
- معرفة شكل البيانات القادمة من API بوضوح.
- تقليل الأخطاء أثناء كتابة الكود.
- الحصول على اقتراحات أفضل من محرر الكود.
- تسهيل صيانة المشروع عند زيادة حجم التطبيق.
- جعل الكود أكثر تنظيمًا ووضوحًا.
ملاحظات مهمة على الكود
الكود السابق مناسب كبداية لفهم فكرة جلب البيانات من API، لكن في المشاريع الحقيقية من الأفضل تحسينه أكثر بإضافة معالجة للأخطاء باستخدام catch، وكذلك إنشاء حالة خاصة للتحميل بدل الاعتماد فقط على طول المصفوفة.
على سبيل المثال، إذا كان API يرجع مصفوفة فارغة فعلًا، فسيظل التطبيق يعرض Loading... رغم أن الطلب انتهى. لذلك في التطبيقات الأكبر من الأفضل استخدام حالة مستقلة مثل loading.
نسخة محسنة بإضافة loading و error
يمكن تحسين الكود بإضافة حالتين: الأولى لمعرفة حالة التحميل، والثانية لتخزين رسالة الخطأ في حال فشل الطلب.
import { useEffect, useState } from 'react';
interface Product {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
rate: number;
count: number;
};
}
function CounterFc() {
const [products, setProducts] = useState<Product[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then(response => response.json())
.then((data) => {
setProducts(data);
setLoading(false);
})
.catch(() => {
setError('حدث خطأ أثناء جلب البيانات');
setLoading(false);
});
}, []);
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>{error}</p>;
}
return (
<div>
<h2>Products</h2>
{products.map((product) => (
<div key={product.id}>
<img src={product.image} width="100" />
<h3>{product.title}</h3>
<p>${product.price}</p>
</div>
))}
</div>
);
}
export default CounterFc;الفرق بين النسخة الأولى والنسخة المحسنة
في النسخة الأولى كنا نستخدم طول مصفوفة المنتجات لمعرفة هل البيانات وصلت أم لا. هذه الطريقة تعمل في المثال البسيط، لكنها ليست الأفضل دائمًا.
أما في النسخة المحسنة، استخدمنا loading لمعرفة هل الطلب ما زال قيد التنفيذ، واستخدمنا error لمعرفة هل حدث خطأ أثناء جلب البيانات.
بهذه الطريقة يصبح الكود أوضح وأكثر قابلية للاستخدام في المشاريع الحقيقية.
الخلاصة
تعلم جلب البيانات من API في React من المهارات الأساسية التي يحتاجها كل مطور واجهات أمامية. في هذا المثال استخدمنا useEffect لتنفيذ طلب جلب البيانات عند تحميل المكون، واستخدمنا useState لتخزين المنتجات بعد وصولها.
كما استخدمنا map لعرض المنتجات داخل الصفحة، وتعرفنا على أهمية استخدام key عند عرض القوائم. بعد ذلك رأينا كيف يمكن استخدام TypeScript لتعريف شكل البيانات القادمة من API، مما يجعل الكود أكثر وضوحًا وأمانًا.
بشكل عام، هذه الطريقة تُعد من أكثر الطرق شيوعًا في React عند التعامل مع البيانات الخارجية، وهي الأساس الذي تُبنى عليه صفحات المنتجات، المقالات، المستخدمين، ولوحات التحكم في التطبيقات الحقيقية.
لمزيد من المقالات : ماهي Hooks ولماذا عليك البدء في استخدامها في مشايعك داخل Flutter





