البرمجة

إدارة تسجيل الدخول في React

إدارة عملية تسجيل الدخول وتحديث الذاكرة المؤقتة في تطبيقات React

مقدمة عامة

يُعَدّ التعامل مع جلسات المستخدم وتخزين البيانات قصيرة الأمد في الذاكرة المؤقتة (Cache) من أعقد المسائل في هندسة الواجهات الأمامية، ولا سيّما عند بناء تطبيقات React أحادية الصفحة (Single‑Page Applications). تتطلَّب هذه التطبيقات استجابة فورية، أماناً عالياً، وتوازناً بين أداء الواجهة وتجربة المستخدم. لذلك تُعنى هذه الدراسة الموسَّعة بتفصيل معمارية شاملة لإدارة تسجيل الدخول (Authentication) وتحديث الذاكرة المؤقتة، اعتماداً على ممارسات حديثة مثل JSON Web Tokens (JWT)، مكتبة React Query/TanStack Query، طبقة Context API وخواص متقدِّمة في HTTP Caching و Service Workers. يرسِّخ المقال أسس التصميم ثم ينتقل إلى إستراتيجيات التحديث الآني (Revalidation) والتعامل مع حالات نهاية الجلسة، واضعاً اعتبارات أمنية وأخلاقية في الصدارة.


1. البنية المنطقية لتدفق المصادقة

1‑1 تهيئة سياق الجلسة مع Context API

يبدأ أي نظام مصادقة فعّال بإنشاء سياق AuthContext يحتفظ بمعلومات المستخدم الحالي، رمز الوصول (access token)، توقيت انتهاء الصلاحية، ودوال التحديث والتسجيل الخروج. يفصل هذا السياق طبقة العرض عن التفاصيل التنفيذية، مع ضمان إعادة تجسيد (Re‑render) انتقائي للمكوّنات التي تعتمد على بيانات الجلسة فقط.

tsx
// ‎ملف AuthProvider.tsx import { createContext, useContext, useState, useCallback } from "react"; interface AuthState { user: User | null; token: string | null; expiresAt: number | null; } const AuthContext = createContext<{ auth: AuthState; login: (creds: Creds) => Promise<void>; logout: () => void; refresh: () => Promise<void>; }>({} as any); export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [auth, setAuth] = useState<AuthState>(() => loadFromStorage()); const login = useCallback(async (creds: Creds) => { const { token, user, exp } = await api.login(creds); setAndPersist({ user, token, expiresAt: exp }); }, []); const refresh = useCallback(async () => { const { token, exp } = await api.refresh(auth.token); setAndPersist({ ...auth, token, expiresAt: exp }); }, [auth]); const logout = useCallback(() => { clearStorage(); setAuth({ user: null, token: null, expiresAt: null }); }, []); return ( <AuthContext.Provider value={{ auth, login, logout, refresh }}> {children} AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext);

يمكّن هذا الهيكل من الحقن الدلالي (Dependency Injection) ويوفِّر نقطة تحكُّم مركزية للتحقق من صلاحية الجلسة وتجديد الرمز.

1‑2 استخدام JWT وآلية التجديد الصامت

يمثّل JWT حلاً شائعاً بفضل طبيعته عديمة الحالة (Stateless). لكن انتهاء صلاحية الرمز يتطلَّب استراتيجية تجديد صامت (Silent Refresh) لتفادي انقطاع تجربة المستخدم. أفضل الممارسات تقضي بما يلي:

  1. تخزين access token في الذاكرة اللحظية (Memory) أو sessionStorage لا في localStorage للتقليل من خطر سرقة الرموز عبر XSS.

  2. حفظ refresh token في HTTP‑Only Secure Cookie على النطاق نفسه (SameSite=Lax) لتحجيم هجمات CSRF.

  3. إنشاء هوك React مخصَّص يراقب قرب انتهاء الصلاحية ويستدعي ‎refresh()‎ قبل دقيقة واحدة مثلاً.

ts
useEffect(() => { if (!auth.expiresAt) return; const id = setTimeout(refresh, auth.expiresAt * 1000 - Date.now() - 60_000); return () => clearTimeout(id); }, [auth.expiresAt, refresh]);

2. إستراتيجيات التخزين المؤقّت للبيانات

2‑1 React Query بوصفها طبقة Cache منضبطة

توفِّر React Query (حاليّاً TanStack Query) واجهة متقدمة تتحكّم في الجلب (Fetching)، التخزين المؤقَّت، والتحقق من الصحة (Stale‑While‑Revalidate). عند دمجه مع نظام المصادقة، نحصل على:

  • ترابط بين مفاتيح الاستعلام (Query Keys) ومعرِّف المستخدم.

  • إبطال (Invalidate) آلي للبيانات عند تسجيل الخروج.

  • تحديث متفائل يرفع استجابة الواجهة بتعديل الذاكرة قبل تأكيد الخادم.

ts
import { QueryClient } from "@tanstack/react-query"; export const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 1000 * 60 * 5, // 5 دقائق retry: 2, onError: (err) => console.error(err), }, }, });

2‑2 تصميم صلاحيات الطبقات المتداخلة

ينشأ تعارض بين البيانات العامة والبيانات المقصورة على مستخدم معيَّن. لتبديد هذا التداخل نقسّم الذاكرة المؤقتة إلى مستويات:

المستوى المحتوى آلية التخزين فترة الصلاحية متى يُبطل؟
عام (Public) إعدادات واجهة، قوائم مفتوحة Service Worker Cache Storage 24 ساعة عند تحديث الإصدار
محلي (Per‑User) ملفّ شخصي، تفضيلات React Query (Memory) 5 دقائق عند الخروج أو تغيير الحساب
فوري (Ephemeral) بيانات نموذج قيد التحرير حالة مكوّن (State) حتى إغلاق التبويب عند النقل بين الصفحات

3. التحقق من الصلاحية والتحديات الأمنية

3‑1 حماية نقاط النهاية وطلبات الجلب

يجب تمرير رمز الوصول في ترويسة ‎Authorization: Bearer‎ مع كل طلب، مع استخدام ‎axios.interceptors‎ أو ‎fetch‎ مُغلَّف لضمان توحيد الآلية وإدراج معالجة الخطأ 401 تلقائياً.

ts
api.interceptors.response.use( (res) => res, async (error) => { if (error.response?.status === 401) { await refresh(); // محاولة التجديد return api(error.config); // إعادة الإرسال } return Promise.reject(error); } );

3‑2 التصدي لهجمات XSS وCSRF

  • XSS: تفعيل سياسات Content‑Security‑Policy، استخدام DOM Purify عند عرض محتوى غير موثوق، والامتناع عن التخزين الدائم للرموز في ‎localStorage‎.

  • CSRF: ضبط سمة ‎SameSite‎ و ‎HttpOnly‎ للكوكي المخصَّص لـ refresh token، واستخدام رموز مضادّة عند الحاجة لتوافق المتصفحات القديمة.


4. إدارة حالات الحافة (Edge Cases)

4‑1 انتهاء صلاحية الرمز أثناء عدم الاتصال

عند العمل دون إنترنت، تحتفظ React Query بالبيانات المخبّأة، لكن إن حاول المستخدم إجراء عملية تتطلّب صلاحية حالية ستفشل. تُوصي الممارسة بإظهار شارة Offline وتخزين العمليات في IndexedDB ثم مزامنتها بعد استعادة الشبكة.

4‑2 السيناريو متعدد الألسنة (Tabs)

تستجيب الأحداث التخزينية ‎storage‎ في المتصفح لأي تغيير في sessionStorage أو localStorage. بتوظيف هذه الأحداث يمكن:

  • إجبار جميع الألسنة على تسجيل الخروج عند تحديث ‎logoutFlag‎.

  • تمرير الرموز المجددة بين الألسنة دون طلب زائد للخادم، عبر قناة BroadcastChannel.


5. دمج Service Workers للذاكرة الدائمة

تتيح Service Workers نمط Cache First لعناصر الواجهة الثابتة، بينما تتكفّل React Query بالبيانات الديناميكية. لضمان توافق النسخ، يُستخدَم ‎postMessage‎ لإعلام الواجهة بتحديث المحتوى الثابت ثم تُحذَّر React Query كي تُعيد التحقق من الصحة.


6. قياس الأداء وتحسينه

  • Time To Interactive (TTI): يقلّ بمقدار 20‑30 % عند تطبيق تخزين مسبق (Prefetch) مع React Query.

  • عدد الرحلات (Round‑Trips): ينخفض بواقع 40‑50 % باستخدام إستراتيجية Stale‑While‑Revalidate.

  • مؤشّر أمان (Security Score): يرتفع عند اعتماد تخزين نسخة الوصول في الذاكرة فقط.


7. خطوات تنفيذية لإطلاق الإنتاج

  1. رصد الثغرات الآنية عبر أدوات مثل OWASP ZAP و Snyk.

  2. فحص الأداء باستخدام Lighthouse CI في خطوط النشر المستمر.

  3. توثيق واجهات API بمنصة OpenAPI وربطها بالعميل عبر Swagger Codegen لتجنّب اختلاف التعاقدات.

  4. مراقبة الجلسات باللوحات المركزية Prometheus + Grafana لتتبّع نسبة أخطاء 401 وتجديد الرموز.

  5. تدريب الفريق على ممارسات الأمن الهجيني (Hybrid Security Practices) مستعينين بأدلة OWASP Top 10.


خاتمة

إن إدارة عملية تسجيل الدخول وتحديث الذاكرة المؤقتة في تطبيقات React تتطلَّب رؤية شمولية تجمع بين تصميم معماري محكم، إجراءات أمنية صارمة، وتقنيات تحسين أداء متقدمة. يقدِّم هذا المقال خارطة طريق عملية تؤسِّس لبناء تطبيقات موثوقة، سريعة، وآمنة، قادرة على تلبية توقعات المستخدم العصري وتحديات بيئات الإنتاج واسعة النطاق. بتطبيق هذه المبادئ، يمكن للمطوِّرين ضمان تجربة سلسة ومستدامة تُعزِّز الثقة وتحافظ على موارد الخادم والمتصفح على حد سواء.


المراجع

  1. Stenberg, J. TanStack Query Docs – Data Synchronization Patterns. إصدار 2025.

  2. OWASP Foundation. OWASP Cheat Sheet Series – JSON Web Token Best Practices. تحديث 2024.