ما وراء الـ Hook: هندسة الـ Fine-Grained Reactivity والـ Signals في تطبيقات Expo الشاملة
استكشاف لتجاوز دورة إعادة التصيير (re-render cycle) في React باستخدام الـ Signals لتحسين الأداء عبر الويب، iOS، و Android. تعلم كيفية تنفيذ الـ fine-grained reactivity في تطبيقات Expo عالية الأداء.

لسنوات، كان سير عملي في React و Expo مرتبطاً بالـ hook. كلمات مثل useState و useEffect و useMemo هي الأدوات الأساسية في مهنتنا. ولكن مع دفع تطبيقات Expo إلى مناطق أكثر تعقيداً وكثافة في البيانات (مثل لوحات تحكم التداول اللحظية أو السبورات التفاعلية التعاونية)، بدأت أواجه سقفاً للأداء. عملية الـ reconciliation، رغم عبقريتها، تصبح عنق زجاجة (bottleneck) عندما تدير مئات من نقاط البيانات المتغيرة باستمرار عبر الويب و iOS و Android.
في مشاريعي الأخيرة، بدأت أبتعد عن حالة المكونات (component-level state) التقليدية نحو نموذج استجابة دقيق يُعرف بـ Fine-grained reactivity باستخدام الـ Signals. إليك لماذا يعد هذا التحول تغييراً جذرياً في الهندسة الشاملة (universal engineering).
المشكلة: ضريبة الـ Re-render
في تطبيق Expo القياسي، عندما تقوم بتحديث جزء من الـ state في مكون أب، تقوم React بمهمتها المعتادة: إعادة تصيير المكون وأطفاله، حساب الفرق (diffing)، وتحديث الـ native views. على هاتف iPhone حديث، قد لا تلاحظ ذلك. ولكن على جهاز Android متوسط المدى أو متصفح بمواصفات منخفضة، تصبح خسارة الإطارات (frame drops) حقيقة ملموسة.
غالباً ما نحاول إصلاح ذلك باستخدام memo ، لكن لـ memo تكاليفها الخاصة أيضاً. نحن في الأساس ندفع "ضريبة" للحفاظ على واجهة المستخدم متزامنة مع الـ state الخاص بنا.
دخول الـ Signals
تمثل الـ Signals نموذجاً عقلياً مختلفاً. بدلاً من القيمة التي تؤدي إلى تحديث المكون بالكامل، فإن الـ Signal هو كائن قابل للمراقبة (observable object) يحمل قيمة. عندما تتغير هذه القيمة، يتم تحديث الجزء المحدد فقط من واجهة المستخدم الذي "يشترك" (subscribes) في هذا الـ signal. المكون نفسه لا يعيد التصيير بالضرورة.
في عالم Expo، هذا أمر ضخم. باستخدام مكتبة مثل @legendapp/state أو Preact Signals (المعدلة لـ React)، يمكننا تحديث نص أو شفافية (opacity) لعنصر ما دون المساس بـ React Fiber tree.
هندسة الحل
دعونا نلقي نظرة على مثال عملي. تخيل موجزاً للأسعار في الوقت الفعلي في تطبيق شامل. باستخدام الـ hooks التقليدية، سيؤدي كل تحديث (tick) إلى إعادة تصيير الصف بالكامل.
الآن، انظر إلى النهج القائم على الـ Signal باستخدام Legend-State، والذي وجدته فعالاً بشكل لا يصدق في بيئات Expo:
الاختراق الشامل: تجاوز الـ Bridge
يحدث السحر الحقيقي عندما نجمع بين الـ signals و react-native-reanimated. في التطبيقات الشاملة، غالباً ما يموت الأداء عند الـ bridge (طبقة التواصل بين JS و Native).
لقد كنت أجرب أنماط "Signal-to-Native" حيث يقوم الـ signal مباشرة بتشغيل Shared Value. يتيح لنا ذلك الاحتفاظ بالمنطق في state machine تفاعلية وسهلة القراءة مع ضمان أن UI thread يتولى المهام الثقيلة على الهاتف المحمول، بينما يتعامل الـ DOM معها بكفاءة على الويب.
لماذا يهم هذا لمجتمع Expo
- استهلاك أقل للبطارية: وقت أقل للمعالجة (CPU) في عملية الـ reconciliation يعني عمر بطارية أفضل على الأجهزة المحمولة.
- تكافؤ الويب (Web Parity): أصبحت الـ fine-grained reactivity هي المعيار على الويب (SolidJS, Qwik, Vue). جلب هذا إلى Expo يضمن أن تطبيقاتنا الشاملة لن تتخلف عن تحسينات الأداء الخاصة بالويب فقط.
- تنظيم إدارة الحالة: تسمح لنا الـ signals برفع الـ state خارج شجرة المكونات دون تعقيدات Redux أو جحيم الـ context في الـ providers المتداخلة بكثافة.
التحول في العقلية
الانتقال إلى الـ signals لا يقتصر فقط على تغيير استدعاء دالة؛ بل يتعلق بالتفكير في التدفقات (Streams) بدلاً من اللقطات (Snapshots). بدلاً من أن تقوم React بأخذ لقطة من الـ state وتحديد ما تغير، فإنك تحدد بوضوح مسار التغيير من مصدر البيانات إلى عقدة واجهة المستخدم.
في تجربتي، يتم تعويض منحنى التعلم الأولي على الفور تقريباً من خلال غياب جلسات تصحيح الأخطاء من نوع "لماذا أعيد تصيير هذا المكون؟". إذا كنت تبني تطبيقات شاملة عالية الأداء باستخدام Expo، فقد حان الوقت للنظر لما وراء الـ hook. المستقبل هو للـ fine-grained.