مكونات الرتبة الأعلى: نمط تصميم تطبيق رد الفعل

هذه المقالة كتبها ضيف جاك فرانكلين. تهدف منشورات ضيف SitePoint إلى تزويدك بمحتوى جذاب من كتاب ومتحدثين بارزين في مجتمع JavaScript

في هذه المقالة ، سنناقش كيفية استخدام المكونات عالية الترتيب للحفاظ على تطبيقات React مرتبة ومنظمة بشكل جيد وسهلة الصيانة. سنناقش كيف تحافظ الوظائف النقية على الكود نظيفًا وكيف يمكن تطبيق هذه المبادئ نفسها على مكونات React.

وظائف خالصة

تعتبر الوظيفة نقية إذا التزمت بالخصائص التالية:

  • يتم الإعلان عن جميع البيانات التي تتعامل معها كوسيطات
  • لا يغير البيانات التي تم تقديمها أو أي بيانات أخرى (يشار إليها غالبًا باسم آثار جانبية)
  • بالنظر إلى نفس المدخلات ، فإنه سوف دائما إرجاع نفس الإخراج.

على سبيل المثال ، ملف add الوظيفة أدناه نقية:

function add(x, y) {
  return x + y;
}

ومع ذلك ، فإن الوظيفة badAdd أدناه نجس:

let y = 2;
function badAdd(x) {
  return x + y;
}

هذه الوظيفة ليست نقية لأنها تشير إلى بيانات لم يتم تقديمها مباشرة. نتيجة لذلك ، من الممكن استدعاء هذه الوظيفة بنفس المدخلات والحصول على مخرجات مختلفة:

let y = 2;
badAdd(3) 
y = 3;
badAdd(3) 

لقراءة المزيد عن الوظائف البحتة ، يمكنك قراءة “مقدمة للبرمجة الخالصة بشكل معقول” بقلم مارك براون.

في حين أن الوظائف الصرفة مفيدة للغاية ، وتجعل تصحيح الأخطاء واختبار التطبيق أسهل بكثير ، فستحتاج أحيانًا إلى إنشاء وظائف غير نقية لها آثار جانبية ، أو تعديل سلوك وظيفة موجودة لا يمكنك الوصول إليها مباشرة (وظيفة من مكتبة ، على سبيل المثال). لتمكين هذا ، نحتاج إلى النظر في وظائف ذات ترتيب أعلى.

وظائف عالية المستوى

دالة الترتيب الأعلى هي دالة تقوم بإرجاع دالة أخرى عند استدعائها. غالبًا ما يأخذون أيضًا دالة كوسيطة ، لكن هذا ليس مطلوبًا حتى يتم اعتبار الوظيفة ذات ترتيب أعلى.

لنفترض أن لدينا add وظيفة من أعلى ، ونريد كتابة بعض التعليمات البرمجية بحيث عندما نسميها ، نسجل النتيجة في وحدة التحكم قبل إرجاع النتيجة. نحن غير قادرين على تحرير add دالة ، لذلك يمكننا بدلاً من ذلك إنشاء وظيفة جديدة:

function addAndLog(x, y) {
  const result = add(x, y);
  console.log(`Result: ${result}`);
  return result;
}

قررنا أن تسجيل نتائج الوظائف مفيد ، والآن نريد أن نفعل الشيء نفسه مع أ subtract وظيفة. بدلاً من تكرار ما ورد أعلاه ، يمكننا كتابة أ وظيفة ذات ترتيب أعلى يمكن أن تأخذ وظيفة وتعيد وظيفة جديدة تستدعي الوظيفة المعينة وتسجيل النتيجة قبل إعادتها:

function logAndReturn(func) {
  return function(...args) {
    const result = func(...args)
    console.log('Result', result);
    return result;
  }
}

يمكننا الآن استخدام هذه الوظيفة لإضافة تسجيل إلى add و subtract:

const addAndLog = logAndReturn(add);
addAndLog(4, 4) 

const subtractAndLog = logAndReturn(subtract);
subtractAndLog(4, 3) 

logAndReturn هي دالة ذات ترتيب أعلى لأنها تأخذ دالة كوسيطة لها وتعيد دالة جديدة يمكننا استدعاءها. هذه مفيدة حقًا لتغليف الوظائف الحالية التي لا يمكنك تغييرها في السلوك. لمزيد من المعلومات حول هذا الموضوع ، راجع مقالة M. David Green “وظائف ذات الترتيب الأعلى في JavaScript” ، والتي تتناول المزيد من التفاصيل حول هذا الموضوع.

انظر القلم
وظائف الترتيب الأعلى
بواسطة SitePoint (تضمين التغريدة)
على كود.

مكونات عالية المستوى

بالانتقال إلى أرض React ، يمكننا استخدام نفس المنطق المذكور أعلاه لأخذ مكونات React الحالية ومنحها بعض السلوكيات الإضافية.

ملاحظة: مع إدخال رد الفعل هوكس، الذي تم إصداره في React 16.8 ، أصبحت الوظائف ذات الترتيب الأعلى أقل فائدة إلى حد ما لأن الخطافات أتاحت مشاركة السلوك دون الحاجة إلى مكونات إضافية. ومع ذلك ، فإنها لا تزال أداة مفيدة في حزامك.

في هذا القسم ، سنستخدم رد فعل راوتر، حل التوجيه الفعلي لـ React. إذا كنت ترغب في البدء في استخدام المكتبة ، فإنني أوصي بشدة بـ وثائق جهاز التوجيه رد الفعل كأفضل مكان للبدء.

تفاعل مكون ارتباط جهاز التوجيه

يوفر React Router ملف <NavLink> المكون المستخدم للربط بين الصفحات في تطبيق React. ومن خصائص هذا <NavLink> يأخذ المكون هو activeClassName. عندما <NavLink> هذه الخاصية وهي نشطة حاليًا (المستخدم موجود على عنوان URL يشير إليه الرابط) ، سيتم منح المكون هذه الفئة ، مما يتيح للمطور تصميمه.

هذه ميزة مفيدة حقًا ، وفي تطبيقنا الافتراضي قررنا أننا نريد دائمًا استخدام هذه الخاصية. ومع ذلك ، بعد القيام بذلك ، اكتشفنا بسرعة أن هذا يجعل كل ما لدينا <NavLink> مكونات مطولة جدا:

<NavLink to="/" activeClassName="active-link">Home</NavLink>
<NavLink to="/about" activeClassName="active-link">About</NavLink>
<NavLink to="/contact" activeClassName="active-link">Contact</NavLink>

لاحظ أنه يتعين علينا تكرار خاصية اسم الفئة في كل مرة. لا يؤدي هذا إلى جعل مكوناتنا مطولة فحسب ، بل يعني أيضًا أنه إذا قررنا تغيير اسم الفصل ، فعلينا القيام بذلك في العديد من الأماكن.

بدلاً من ذلك ، يمكننا كتابة مكون يلف ملف <NavLink> مكون:

const AppLink = (props) => {
  return (
    <NavLink to={props.to} activeClassName="active-link">
      {props.children}
    </NavLink>
  );
};

والآن يمكننا استخدام هذا المكون ، الذي ينظم روابطنا:

<AppLink to="/home" exact>Home</AppLink>
<AppLink to="/about">About</AppLink>
<AppLink to="/contact">Contact</AppLink>

في النظام البيئي React ، تُعرف هذه المكونات بالمكونات ذات الترتيب الأعلى ، لأنها تأخذ مكونًا موجودًا وتتلاعب به قليلاً دون تغيير المكون الحالي. يمكنك أيضًا التفكير في هذه على أنها مكونات مغلفة ، لكنك ستجدها يشار إليها عادةً باسم مكونات ذات ترتيب أعلى في المحتوى القائم على React.

مكونات أفضل ذات ترتيب أعلى

المكون أعلاه يعمل ، ولكن يمكننا القيام بعمل أفضل بكثير. ال <AppLink> المكون الذي أنشأناه ليس مناسبًا تمامًا للغرض.

قبول خصائص متعددة

ال <AppLink> يتوقع المكون خاصيتين:

  • this.props.to، وهو عنوان URL الذي يجب أن يأخذ الرابط المستخدم إليه
  • this.props.children، وهو النص الذي يظهر للمستخدم

ومع ذلك ، فإن <NavLink> يقبل المكون العديد من الخصائص ، وقد يكون هناك وقت ترغب فيه في تمرير خصائص إضافية مع الاثنين أعلاه ، والتي نريد دائمًا تمريرها. نحن لم نصنع <AppLink> قابلة للتوسيع جدًا عن طريق الترميز الثابت للخصائص الدقيقة التي نحتاجها.

انتشار JSX

JSX ، البنية التي تشبه HTML التي نستخدمها لتعريف عناصر React ، تدعم صيغة انتشار المشغل لتمرير كائن إلى مكون كخصائص. على سبيل المثال ، تحقق عينات الكود أدناه نفس الشيء:

const props = { a: 1, b: 2};
<Foo a={props.a} b={props.b} />

<Foo {...props} />

باستخدام {...props} ينشر كل مفتاح في الكائن ويمرره إلى Foo كملكية فردية.

يمكننا الاستفادة من هذه الحيلة مع <AppLink> لذلك نحن نؤيد أي خاصية تعسفية <NavLink> يدعم. من خلال القيام بذلك نحن أيضًا نثبت أنفسنا في المستقبل ؛ إذا <NavLink> يضيف أي خصائص جديدة في المستقبل سيدعمها مكون الغلاف بالفعل.

const AppLink = (props) => {
  return <NavLink {...props} activeClassName="active-link" />;
}

الآن <AppLink> سيقبل أي خصائص ويمررها. لاحظ أنه يمكننا أيضًا استخدام نموذج الإغلاق الذاتي بدلاً من الإشارة صراحةً {props.children} بين ال <NavLink> العلامات. React يسمح children لتمريرها كعنصر عادي أو كعناصر فرعية لمكون بين علامة الفتح والإغلاق.

ترتيب الخاصية في React

تخيل أنه بالنسبة لرابط معين على صفحتك ، يجب عليك استخدام ملف activeClassName. حاولت تمريره إلى <AppLink>، حيث نمرر جميع العقارات من خلال:

<AppLink to="/special-link" activeClassName="special-active">Special Secret Link</AppLink>

ومع ذلك ، هذا لا يعمل. يرجع السبب في ذلك إلى ترتيب الخصائص عند تقديم ملف <NavLink> مكون:

return <Link {...props} activeClassName="active-link" />;

عندما يكون لديك نفس الخاصية عدة مرات في مكون React ، فإن ملف الإعلان الأخير يفوز. هذا يعني أن لدينا آخر activeClassName="active-link" الإعلان سيفوز دائمًا ، منذ وضعه بعد {...this.props}. لإصلاح ذلك ، يمكننا إعادة ترتيب الخصائص حتى ننتشر this.props الاخير. هذا يعني أننا نضع الإعدادات الافتراضية المعقولة التي نرغب في استخدامها ، ولكن يمكن للمستخدم تجاوزها إذا احتاج حقًا إلى:

return <Link activeClassName="active-link" {...props}  />;

من خلال إنشاء مكونات ذات ترتيب أعلى تغلف المكونات الموجودة ولكن بسلوك إضافي ، نحافظ على نظافة قاعدة الكود لدينا وندافع ضد التغييرات المستقبلية من خلال عدم تكرار الخصائص والحفاظ على قيمها في مكان واحد فقط

صانعي المكونات من الدرجة الأولى

غالبًا ما يكون لديك عدد من المكونات التي ستحتاج إلى لفها بنفس السلوك. هذا مشابه جدًا لما ورد سابقًا في هذه المقالة عندما انتهينا add و subtract لإضافة تسجيل الدخول لهم.

دعنا نتخيل أن لديك في التطبيق الخاص بك كائنًا يحتوي على معلومات عن المستخدم الحالي الذي تمت مصادقته على النظام. أنت بحاجة إلى بعض مكونات React الخاصة بك لتتمكن من الوصول إلى هذه المعلومات ، ولكن بدلاً من جعلها متاحة بشكل أعمى لكل مكون تريد أن تكون أكثر صرامة بشأن المكونات التي تتلقى المعلومات.

طريقة حل هذه المشكلة هي إنشاء دالة يمكننا استدعاؤها بمكوِّن React. ستعيد الوظيفة بعد ذلك مكون React جديدًا سيعرض المكون المحدد ولكن بخاصية إضافية ستمنحه إمكانية الوصول إلى معلومات المستخدم.

هذا يبدو معقدًا جدًا ، لكنه أصبح أكثر وضوحًا ببعض التعليمات البرمجية:

function wrapWithUser(Component) {
  
  const secretUserInfo = {
    name: 'Jack Franklin',
    favouriteColour: 'blue'
  };

  
  
  return function(props) {
    
    
    return <Component user={secretUserInfo} {...props} />
  }
}

تأخذ الوظيفة مكون React (الذي يسهل اكتشافه نظرًا لأن مكونات React عادةً ما تحتوي على أحرف كبيرة في البداية) وتعيد وظيفة جديدة تجعل المكون الذي تم إعطاؤه بخاصية إضافية user، والذي تم تعيينه على secretUserInfo.

لنأخذ الآن مكونًا ، <AppHeader>، التي تريد الوصول إلى هذه المعلومات حتى تتمكن من عرض المستخدم الذي قام بتسجيل الدخول:

const AppHeader = function(props) {
  if (props.user) {
    return <p>Logged in as {props.user.name}</p>;
  } else {
    return <p>You need to login</p>;
  }
}

الخطوة الأخيرة هي توصيل هذا المكون حتى يتم إعطاؤه this.props.user. يمكننا إنشاء مكون جديد بتمرير هذا المكون في wrapWithUser وظيفة:

const ConnectedAppHeader = wrapWithUser(AppHeader);

لدينا الآن ملف <ConnectedAppHeader> المكون الذي يمكن تقديمه ، وسيكون له حق الوصول إلى user موضوع.

اخترت استدعاء المكون ConnectedAppHeader لأنني أفكر في الأمر على أنه متصل ببعض البيانات الإضافية التي لا يُسمح لكل مكون بالوصول إليها.

هذا النمط شائع جدًا في مكتبات React ، لا سيما في ملفات إعادة، لذا فإن إدراكك لكيفية عمله وأسباب استخدامه سيساعدك مع نمو تطبيقك وتعتمد على مكتبات الجهات الخارجية الأخرى التي تستخدم هذا الأسلوب.

خاتمة

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

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

إذا كان لديك أي أسئلة ، فأنا أحب سماعها. لا تتردد في الاتصال بي تضمين التغريدة على تويتر.

المصدر

أضف تعليق