ما وراء الـ Re-Render: هندسة التفاعلية المعتمدة على الـ Signals في تطبيقات Expo عالية الأداء
توقف عن صراع دورة حياة React من أجل التحديثات عالية التردد. اكتشف كيف أستخدم التفاعلية المبنية على الـ Signals لتجاوز شجرة المكونات وتحقيق 120FPS في تطبيقات Expo المعقدة.

قضيت معظم العقد الماضي في محاولة استخراج أقصى أداء ممكن من React Native. لقد مررنا جميعًا بهذا الموقف: أنت تقوم ببناء لوحة تحكم معقدة أو موجز بيانات آنية (telemetry) في Expo، وفجأة، ينخفض معدل الإطارات (frame rate). تبدأ في توزيع useMemo و useCallback كأنها ماء مقدس، لكن المشكلة الأساسية تظل قائمة — نموذج الـ reconciliation في React هو عنق زجاجة للتحديثات المتكررة للبيانات.
مؤخرًا، بدأت في الابتعاد عن أنماط إدارة الحالة (state management) التقليدية للواجهات الحساسة للأداء. الاختراق الحقيقي؟ الـ Signals.
المشكلة: صراع مزامنة الإطارات (V-Sync Struggle)
في تطبيقات React Native التقليدية، عندما تتغير حالة (state) معينة، يتم إعادة رندرة (re-render) المكون (وربما المكونات التابعة له). يقوم React بإنتاج شجرة عناصر جديدة، ويقارنها (diffs)، ثم يرسل الأوامر عبر الـ bridge (أو عبر JSI) لتحديث الواجهات الأصلية (native views).
عندما تتعامل مع تدفق بيانات من المستشعرات (sensor stream) أو شريط أسعار أسهم سريع، فإن القيام بذلك 60 مرة في الثانية مكلف للغاية. حتى مع الـ New Architecture (Fabric)، يمكن أن يستهلك العبء الإضافي لدورة حياة React ميزانية الإطارات المتاحة لك. وجدت نفسي أتساءل: لماذا أعيد رندرة مكون كامل لمجرد تغيير قيمة نصية واحدة؟
الحل: التفاعلية دقيقة التفاصيل (Fine-Grained Reactivity)
تغير الـ Signals النموذج الذهني. بدلاً من أن 'يستهلك' المكون الحالة، فإن الـ signal 'يستهدف' عقدة محددة في واجهة المستخدم. عندما تتغير قيمة الـ signal، فإنه يحدث الواجهة مباشرة، متجاوزًا عملية الـ reconciliation في React تمامًا لهذا التحديث.
في مشاريع Expo الأخيرة، كنت أستخدم @preact/signals-react. إنها مكتبة خفيفة الوزن وتتكامل بشكل مدهش مع بيئة تشغيل React Native.
تنفيذ الـ Signals في Expo
إليك نمطًا كنت أستخدمه للتعامل مع تدفق بيانات عالي التردد — فكر في عرض إحداثيات GPS أو مراقب معدل ضربات القلب في الوقت الفعلي.
لماذا ينجح هذا معنا؟
عندما تقوم بتشغيل الـ profiler على هذا الكود، ستلاحظ شيئًا رائعًا: يتم رندرة HighPerformanceScreen مرة واحدة فقط. تحدث تحديثات مكون الـ Text عبر اشتراك مباشر (direct subscription). من خلال تجنب عملية الـ diffing في الـ virtual DOM، رأيت انخفاضًا في حمل خيط الـ JS (JS thread) بنسبة تصل إلى 40% في الواجهات المليئة بالبيانات.
ربط الـ Signals مع Reanimated
كان أحد أكبر تحدياتي هو جعل الـ signals تعمل بانسجام مع react-native-reanimated. نظرًا لأن Reanimated يعمل على خيط واجهة المستخدم (UI thread) بينما الـ signals (عادةً) تعيش في خيط الـ JS، يجب أن تكون دقيقًا في تنفيذك.
وجدت أن استخدام الـ signals كـ "مصدر للحقيقة" للمنطق البرمجي (business logic) ثم مزامنتها مع SharedValue فقط عند بدء الرسوم المتحركة (animation) يوفر أفضل ما في العالمين: تحديثات واجهة مستخدم دقيقة للنصوص والبيانات، ورسوم متحركة مدعومة بالعتاد (hardware-accelerated) للحركة.
دروس من الميدان
إذا كنت تنوي تجربة هذا في مشروع Expo القادم، فضع هذه الأمور الثلاثة في اعتبارك:
- لا تفرط في استخدام الـ signals: استخدمها للتحديثات عالية التردد. بالنسبة للتنقل العادي أو حالات النماذج (forms)، لا يزال
useStateأوZustandخيارًا ممتازًا وأكثر قابلية للتوقع لمعظم الفرق. - التصحيح (Debugging) مختلف: لن تظهر لك أدوات React DevTools التقليدية سبب تغير القيمة المعتمدة على الـ signal، لأن المكون لم يعد رندرته. ستحتاج للاعتماد أكثر على التسجيل (logging) داخل ميثود
.subscribeالخاصة بالـ signal. - النظام البيئي يلحق بالركب: نشهد تحولاً هائلاً. مع ظهور React Compiler (React Forget)، قد تصبح بعض هذه التحسينات اليدوية قديمة، ولكن في الوقت الحالي، توفر الـ signals مستوى من الدقة الجراحية التي لا يستطيع المترجم (compiler) مضاهاتها بعد في بيئة React Native.
الخاتمة
الانتقال "إلى ما وراء الـ Re-render" لا يعني التخلي عن React؛ بل يتعلق بمعرفة متى تعمل الأداة ضد العتاد. في عالم أداء تطبيقات الجوال عالي المخاطر، تعتبر الـ signals أقوى سلاح أضفته إلى ترسانتي هذا العام. إذا كنت تبني تطبيقات باستخدام Expo وتشعر بالبطء، فقد يكون الوقت قد حان للتوقف عن الـ re-rendering والبدء في استخدام الـ signals.