أهلاً بيك يا صديقي المبرمج في مقال جديد. لو طُلب منك قبل كده تبني تطبيق ويب فيه خريطة، أكيد عشت اللحظة اللي بتحس فيها إن الشاشات بتهنج، والـ Browser بيطلع صوت غريب من كتر الـ Memory Leaks! التعامل مع الخرائط المعقدة (Complex Maps) وتحديداً لما نتكلم عن عمالقة زي (Mapbox) أو خرائط جوجل (Google Maps) جوه رياكت (React)، دايمًا بيكون كابوس لأي مطور frontend لو مش ماشي على خطة واضحة.
في المقال ده، هناخد بايدك خطوة بخطوة إزاي تتعامل مع الخرائط دي باحترافية، تضيف دبابيس (Markers)، ترسم مسارات (Polylines)، والأهم من ده كله: إزاي تحافظ على أداء عالي (High Performance) من غير ما التطبيق يهنق مع المستخدم.
Table of contents [Show]
ليه التعامل مع الخرائط في (React) ممكن يكون معقد؟
المشكلة الأساسية يا سيدي إن مكتبات الخرائط أصلاً مكتوبة بلغة الجافاسكريبت الفانيلا (Vanilla JavaScript) وبتتلاعب بالـ DOM بشكل مباشر (Direct DOM Manipulation). في المقابل، رياكت (React) شغالة بحاجة اسمها الـ Virtual DOM وبتدير الحالة (State) بطريقتها الخاصة.
الصدام ده بيعمل مشاكل كبيرة زي إعادة الرندر (Re-rendering) الكتير، وضياع الحالة بتاعت الخريطة (Map State) كل ما مكون (Component) يتحدث. الحل هنا إننا نفهم إزاي نربط دورة حياة رياكت (React Lifecycle) مع أحداث الخريطة صح.
الخطوة الأولى: اختيار السلاح المناسب (Mapbox vs Google Maps)
قبل ما تكتب أي كود، لازم تقرر أنت محتاج إيه:
- خرائط جوجل (Google Maps API): ممتازة جداً لو تطبيقك محتاج بحث أماكن دقيق جداً (Places API) ومعلومات مرور لحظية، بستكون أثقل شوية في التحميل لو الأداء مش محسوب صح.
- ماب ب็อกس (Mapbox GL JS): تحفة فنية في الأداء (Performance) وبتعتمد على الـ WebGL بشكل كامل، وشكلها بيكون عصري وجميل ومخصصة أكتر للتصميمات المعقدة.
عشان نشتغل بصورة صحيحة مع رياكت، بنفضل نستخدم مكتبات جاهزة ومخصصة زي react-map-gl لو شغالين Mapbox، أو @react-google-maps/api لو شغالين جوجل.
إضافة الدبابيس (Markers) وإدارة الحالة التفاعلية
تخيل إن عندك تطبيق توصيل (Delivery App) وعاوز تعرض مواقع السواقين لحظياً على الخريطة. هنا بتظهر أهمية إدارة الـ State صح عشان الخريطة متهنجش.
import React, { useState } from 'react';
import Map, { Marker } from 'react-map-gl';
function DeliveryMap() {
const [viewport, setViewport] = useState({
latitude: 30.0444,
longitude: 31.2357,
zoom: 11
});
const [drivers, setDrivers] = useState([
{ id: 1, lat: 30.05, lng: 31.24 },
{ id: 2, lat: 30.03, lng: 31.22 }
]);
return (
<Map
{...viewport}
onMove={evt => setViewport(evt.viewState)}
mapboxAccessToken="YOUR_MAPBOX_TOKEN"
style={{width: '100%', height: '500px'}}
mapStyle="mapbox://styles/mapbox/streets-v11"
>
{drivers.map(driver => (
<Marker key={driver.id} latitude={driver.lat} longitude={driver.lng}>
<div style={{background: 'red', borderRadius: '50%', padding: '10px'}}>🚗</div>
</Marker>
))}
</Map>
);
}
export default DeliveryMap;
رسم المسارات (Polylines) وتحسين الأداء (Performance Optimization)
لما تبدأ ترسم مسارات (Routes) معقدة بين نقطتين أو أكتر، هنا حجم البيانات بيكبر. لو كل شوية عملت Fetch للمسار أو رندرت الخريطة من أول وجديد، التطبيق هيكون بطيء جداً.
عشان تحافظ على أداء عالي (High Performance):
- استخدم الـ
useMemoوuseCallbackعشان تمنع إعادة حساب إحداثيات المسار بدون داعي. - قلل عدد الـ Markers اللي بتظهر في نفس الوقت باستخدام تقنية الـ Clustering لو العدد كبير جداً (أكتر من 100 دبابيس مثلاً).
- نظف الـ Event Listeners بتاعت الخريطة في الـ
useEffect cleanup functionعشان تمنع الـ Memory Leaks.
خاتمة ونصيحة من أخ
التعامل مع الخرائط في رياكت ممكن يبמו مخيف في الأول، لكن السر كله بيكمن في إنك تفصل ما بين اللوجيك بتاع رياكت ورندرة الخريطة نفسها. متسترخصش وقتك في قراءة الـ Documentation الرسمية للمكتبة اللي هختارها، لأنها دايماً فيها أسرار الأداء العالي.
اتمرن بايدك، ابني مشاريع صغيرة، وغلط وعيد تاني، دي الطريقة الوحيدة عشان تتحول من مبرمج بيجيب كود من ستيك أوفرفلو لمبرمج تقني فاهم هو بيعمل إيه بظبط.