البرمجة

ربط React مع Redux بـ connect

استخدام الدالة connect في Redux في تطبيقات React

تُعدّ مكتبة Redux من الأدوات الأكثر شيوعًا لإدارة الحالة في تطبيقات React، إذ توفّر وسيلة مركزية لتخزين الحالة ومشاركتها بين مكونات مختلفة في واجهة المستخدم. إحدى اللبنات الأساسية في الدمج بين React وRedux هي الدالة connect، التي تتيح ربط مكونات React بمخزن الحالة (Redux store) دون أن تحتاج المكونات إلى معرفة تفاصيل آلية التخزين أو بنية الحالة العامة. يُعد استخدام connect خطوة محورية في تسهيل إنشاء تطبيقات متسقة وقابلة للصيانة.


المفهوم الأساسي لـ Redux

قبل التعمق في تفاصيل connect، من المهم أولًا فهم المبادئ التي ترتكز عليها مكتبة Redux. تقوم Redux على ثلاث مبادئ أساسية:

  1. مصدر وحيد للحقيقة (Single Source of Truth): يُخزَّن كامل حالة التطبيق في كائن JavaScript واحد داخل “store”.

  2. الحالة للقراءة فقط (State is Read-only): لا يمكن تغيير الحالة مباشرة؛ بل يجب إرسال “إجراء” (action) يصف التغيير المطلوب.

  3. التغييرات تتم من خلال دوال نقية (Reducers): تُحدد كيفية انتقال الحالة من وضع إلى آخر من خلال دوال تأخذ الحالة الحالية والإجراء كوسيطين وتُعيد الحالة الجديدة.


العلاقة بين React وRedux

تُبنى React على مفهوم المكونات، حيث يقوم كل مكون بعرض واجهة بناءً على الخصائص (props) والحالة (state). ومع نمو التطبيقات وتعقيد العلاقات بين المكونات، تصبح إدارة الحالة المشتركة بين هذه المكونات أكثر صعوبة. هنا تظهر الحاجة إلى مكتبة مثل Redux، لتوفير مكان واحد تُخزَّن فيه الحالة ويمكن لأي مكون الوصول إليها وتحديثها بطريقة منظمة.

لكن React لا تعرف افتراضيًا عن Redux، ولذلك يلزم وجود وسيط يربط بين الطرفين. هذا الوسيط هو مكتبة react-redux، والتي توفّر بدورها الدالة connect.


ما هي الدالة connect؟

الدالة connect هي دالة من مكتبة react-redux تُستخدم لربط مكونات React بمخزن Redux. وهي تعمل كدالة عالية الترتيب (Higher-Order Component) تأخذ مكونًا وتُعيد مكونًا جديدًا متصلًا بـ store.

الشكل العام:

javascript
connect(mapStateToProps, mapDispatchToProps)(Component)

المعاملات الأساسية:

  • mapStateToProps: دالة تُحدّد أي جزء من حالة Redux يجب تمريره كمُدخلات (props) للمكون.

  • mapDispatchToProps: دالة تُحدّد كيف يمكن للمكون إرسال إجراءات (actions) إلى المخزن لتحديث الحالة.

  • Component: هو مكون React الذي نريد ربطه بـ Redux.


دالة mapStateToProps

هي دالة اختيارية تُستخدم لاختيار الأجزاء من الحالة العامة للتطبيق التي يحتاجها المكون. تأخذ state من المخزن وتُعيد كائنًا يُضاف إلى الخصائص (props) الخاصة بالمكون.

javascript
const mapStateToProps = (state) => ({ counter: state.counter, });

في هذا المثال، يتم تمرير الخاصية counter من الحالة العامة إلى المكون كـ prop.


دالة mapDispatchToProps

هي دالة اختيارية أيضًا، تُتيح للمكون إرسال أحداث (actions) إلى المخزن. يمكن أن تكون دالة أو كائنًا من الإجراءات الجاهزة.

javascript
const mapDispatchToProps = (dispatch) => ({ increment: () => dispatch({ type: 'INCREMENT' }), });

أو باستخدام طريقة مختصرة:

javascript
const mapDispatchToProps = { increment, };

مثال تطبيقي كامل

javascript
// actions.js export const increment = () => ({ type: 'INCREMENT' }); export const decrement = () => ({ type: 'DECREMENT' }); // reducer.js const initialState = { counter: 0 }; const counterReducer = (state = initialState, action) => { switch (action.type) { case 'INCREMENT': return { counter: state.counter + 1 }; case 'DECREMENT': return { counter: state.counter - 1 }; default: return state; } }; export default counterReducer; // CounterComponent.jsx import React from 'react'; const CounterComponent = ({ counter, increment, decrement }) => ( <div> <h1>{counter}h1> <button onClick={increment}>+1button> <button onClick={decrement}>-1button> div> ); export default CounterComponent; // ConnectedCounter.jsx import { connect } from 'react-redux'; import CounterComponent from './CounterComponent'; import { increment, decrement } from './actions'; const mapStateToProps = (state) => ({ counter: state.counter, }); const mapDispatchToProps = { increment, decrement, }; export default connect(mapStateToProps, mapDispatchToProps)(CounterComponent);

آلية العمل خلف الكواليس

عند استدعاء connect(mapStateToProps, mapDispatchToProps)(Component)، تحدث الأمور التالية:

  1. يقوم connect بإنشاء مكون جديد يُسمى “Connected Component”.

  2. هذا المكون يكون مشتركًا في store من خلال Provider.

  3. في كل مرة تتغير فيها الحالة، يتم استدعاء mapStateToProps تلقائيًا لتحديث خصائص المكون.

  4. الدالة mapDispatchToProps تتيح إرسال الإجراءات التي تُحدِث تغييرات في الحالة.


مقارنة بـ useSelector و useDispatch

مع إدخال الـ Hooks في React 16.8، أصبحت هناك طرق بديلة لـ connect من خلال useSelector و useDispatch. إلا أن connect ما تزال مستخدمة على نطاق واسع، خاصة في الأكواد القديمة أو عند التعامل مع مكونات صنفية (Class Components).

الميزة connect useSelector / useDispatch
نوع المكون يُستخدم مع مكونات صنفية ووظيفية يُستخدم فقط مع مكونات وظيفية
الأداء أداء أعلى عند فصل الخصائص أقل مرونة في منع إعادة التصيير
التنظيم يحفّز الفصل بين المنطق والعرض يُدمج المنطق داخل المكون مباشرةً
التوافق متوافق مع الأنظمة القديمة والكبيرة أحدث، ويُفضل في مشاريع جديدة

تحسين الأداء باستخدام connect

تتيح connect تحسين أداء تطبيقات React عند استخدامها بشكل مدروس، وذلك من خلال:

  1. فصل الحالة المطلوبة فقط: يمكن لمكون ما الاستماع فقط لأجزاء محددة من الحالة، مما يمنع إعادة التصيير غير الضرورية.

  2. استخدام ownProps: تسمح بتعديل المخرجات بناءً على الخصائص الخارجية.

  3. استخدام areStatesEqual و areOwnPropsEqual: يمكن تخصيص مقارنة الحالة أو الخصائص لتجنب إعادة التصيير غير الضروري.


تنظيم الملفات في مشروع يستخدم connect

من الأفضل عند استخدام connect تقسيم الملفات والمجلدات بطريقة منظمة، مثل:

bash
/src /components CounterComponent.jsx /containers ConnectedCounter.jsx /redux /actions counterActions.js /reducers counterReducer.js store.js

يُعزّز هذا التنظيم من إمكانية إعادة الاستخدام، وسهولة الفهم والصيانة.


تحديات وصعوبات محتملة عند استخدام connect

  • تعقيد الربط في المكونات الكبيرة: كلما زاد تعقيد mapStateToProps وmapDispatchToProps، زادت صعوبة تتبع التحديثات.

  • صعوبة تتبع الأخطاء: عند وجود عدة مكونات مرتبطة بنفس الجزء من الحالة، قد يكون من الصعب تحديد المكون المسبب لتغيير غير متوقع.

  • الإفراط في استخدام connect: يُفضل عدم ربط كل مكون بـ store؛ بل استخدام مبدأ “الوعاء والمحتوى” (container/presentational components).


متى يُفضّل استخدام connect؟

رغم الاتجاه الحديث نحو استخدام Hooks، إلا أن connect تبقى خيارًا موثوقًا في الحالات التالية:

  • وجود مكونات صنفية (Class Components).

  • الحاجة إلى تحسين الأداء وتقليل إعادة التصيير.

  • وجود قاعدة شيفرة كبيرة تستخدم connect بالفعل.

  • وجود منطق معقّد في mapStateToProps يصعب ترجمته بـ useSelector.


الخلاصة

تُعتبر الدالة connect أحد المفاتيح الأساسية للدمج بين React وRedux، إذ توفّر وسيلة فعّالة ومنظمة لربط المكونات بحالة التطبيق. ورغم ظهور تقنيات أحدث كـ Hooks، إلا أن connect لا تزال تُستخدم على نطاق واسع في التطبيقات الكبيرة والمشاريع ذات البنية التقليدية. من خلال فهم عميق لكيفية عمل connect، يمكن بناء تطبيقات قوية، مرنة وقابلة للصيانة طويلة الأمد.


المراجع:

  1. Redux Documentation – React Redux

  2. Redux Official Guide