14 نصائح لكتابة رمز التفاعل النظيف. الجزء 1

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



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

1. تدمير الخصائص


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

تتيح خصائص التدمير أيضًا للمبرمج تعيين قيمه الافتراضية. هذا شائع جدا:

import React from 'react' import Button from 'components/Button' const MyComponent = ({ placeholder = '', style, ...otherProps }) => {   return (     <Button       type="button"       style={{         border: `1px solid ${placeholder ? 'salmon' : '#333'}`,         ...style,       }}       {...otherProps}     >       Click Me     </Button>   ) } export default MyComponent 

واحدة من أكثر النتائج اللطيفة لاستخدام التدمير في JavaScript ، والتي تمكنت من العثور عليها ، أنها تتيح لك دعم خيارات متنوعة للمعلمات.

على سبيل المثال ، لدينا وظيفة authenticate ، والتي اتخذت كمعلمة token المستخدم لمصادقة المستخدمين. في وقت لاحق ، كان من الضروري جعله يقبل الكيان jwt_token . سبب هذه الحاجة هو تغيير في بنية استجابة الخادم. بفضل استخدام التدمير ، يمكن بسهولة تنظيم الدعم لكلا المعلمتين دون الحاجة إلى تغيير معظم كود الوظيفة:

 //   async function authenticate({ user_id, token }) {  try {    const response = await axios.post('https://someapi.com/v1/auth/', {      user_id,      token,    })    console.log(response)    return response.data  } catch (error) {    console.error(error)    throw error  } } //   async function authenticate({ user_id, jwt_token, token = jwt_token }) {  try {    const response = await axios.post('https://someapi.com/v1/auth/', {      user_id,      token,    })    console.log(response)    return response.data  } catch (error) {    console.error(error)    throw error  } } 

سيتم تقييم jwt_token عندما يصل token إلى token . نتيجة لذلك ، إذا تبين أن jwt_token رمز مميز صالح ، وكان الكيان token undefined ، فسوف تقع قيمة jwt_token في token . إذا كان في token بالفعل بعض القيمة التي لم تكن خاطئة بواسطة قواعد JS (أي ، بعض الرموز الحقيقية) ، فعندها سيكون token ما هو موجود بالفعل.

2. ضع ملفات المكونات في هيكل مجلد مدروس جيدًا


ألقِ نظرة على بنية الدليل التالية:

  • SRC

    • المكونات
    • Breadcrumb.js
    • CollapsedSeparator.js
  • مساهمة

    • index.js
    • Input.js
    • utils.js
    • focusManager.js
  • بطاقة

    • index.js
    • Card.js
    • CardDivider.js
  • Button.js
  • Typography.js

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

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

إذا استخدمنا بنية دليل مدروسة في مثالنا ، فسنحصل على شيء مثل التالي:

  • SRC

    • المكونات
  • التفصيلي

    • index.js
    • Breadcrumb.js
    • CollapsedSeparator.js
  • مساهمة

    • index.js
    • Input.js
    • utils.js
    • focusManager.js
  • بطاقة

    • index.js
    • Card.js
    • CardDivider.js
  • Button.js
  • Typography.js

الآن لا يهم عدد المكونات المرتبطة بمكون Breadcrumb سيتم إنشاؤه. طالما توجد ملفاتهم في نفس الدليل مثل Breadcrumb.js ، فسوف نعرف أنها مرتبطة بمكون Breadcrumb :

  • SRC

    • المكونات
  • التفصيلي

    • index.js
    • Breadcrumb.js
    • CollapsedSeparator.js
    • Expander.js
    • BreadcrumbText.js
    • BreadcrumbHotdog.js
    • BreadcrumbFishes.js
    • BreadcrumbLeftOvers.js
    • BreadcrumbHead.js
    • BreadcrumbAddict.js
    • BreadcrumbDragon0814.js
    • BreadcrumbContext.js
  • مساهمة

    • index.js
    • Input.js
    • utils.js
    • focusManager.js
  • بطاقة

    • index.js
    • Card.js
    • CardDivider.js
  • Button.js
  • Typography.js

هذه هي الطريقة التي يظهر بها العمل في الهياكل المماثلة في الكود:

 import React from 'react' import Breadcrumb, {  CollapsedSeparator,  Expander,  BreadcrumbText,  BreadcrumbHotdog,  BreadcrumbFishes,  BreadcrumbLeftOvers,  BreadcrumbHead,  BreadcrumbAddict,  BreadcrumbDragon0814, } from '../../../../../../../../../../components/Breadcrumb' const withBreadcrumbHotdog = (WrappedComponent) => (props) => (  <WrappedComponent BreadcrumbHotdog={BreadcrumbHotdog} {...props} /> ) const WorldOfBreadcrumbs = ({  BreadcrumbHotdog: BreadcrumbHotdogComponent, }) => {  const [hasFishes, setHasFishes] = React.useState(false)  return (    <BreadcrumbDragon0814      hasFishes={hasFishes}      render={(results) => (        <BreadcrumbFishes>          {({ breadcrumbFishes }) => (            <BreadcrumbLeftOvers.Provider>              <BreadcrumbHotdogComponent>                <Expander>                  <BreadcrumbText>                    <BreadcrumbAddict>                      <pre>                        <code>{JSON.stringify(results, null, 2)}</code>                      </pre>                    </BreadcrumbAddict>                  </BreadcrumbText>                </Expander>                {hasFishes                  ? breadcrumbFishes.map((fish) => (                      <>                        {fish}                        <CollapsedSeparator />                      </>                    ))                  : null}              </BreadcrumbHotdogComponent>            </BreadcrumbLeftOvers.Provider>          )}        </BreadcrumbFishes>      )}    />  ) } export default withBreadcrumbHotdog(WorldOfBreadcrumbs) 

3. اسم المكونات باستخدام اصطلاحات التسمية القياسية


باستخدام معايير معينة عندما تسهل مكونات التسمية على شخص ليس مؤلف المشروع قراءة التعليمات البرمجية لهذا المشروع.

على سبيل المثال ، عادة ما تكون بادئة أسماء مكونات الترتيب العالي (HOCs). يتم استخدام العديد من المطورين لأسماء المكونات هذه:

 import React from 'react' import hoistNonReactStatics from 'hoist-non-react-statics' import getDisplayName from 'utils/getDisplayName' const withFreeMoney = (WrappedComponent) => {  class WithFreeMoney extends React.Component {    giveFreeMoney() {      return 50000    }    render() {      return (        <WrappedComponent          additionalMoney={[            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),          ]}          {...this.props}        />      )    }  }  WithFreeMoney.displayName = `withFreeMoney(${getDisplayName(    WrappedComponent,  )}$)`  hoistNonReactStatics(WithFreeMoney, WrappedComponent)  return WithFreeMoney } export default withFreeMoney 

لنفترض أن شخصًا ما قرر التراجع عن هذه الممارسة وعمل ذلك:

 import React from 'react' import hoistNonReactStatics from 'hoist-non-react-statics' import getDisplayName from 'utils/getDisplayName' const useFreeMoney = (WrappedComponent) => {  class WithFreeMoney extends React.Component {    giveFreeMoney() {      return 50000    }    render() {      return (        <WrappedComponent          additionalMoney={[            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),            this.giveFreeMoney(),          ]}          {...this.props}        />      )    }  }  WithFreeMoney.displayName = `useFreeMoney(${getDisplayName(    WrappedComponent,  )}$)`  hoistNonReactStatics(WithFreeMoney, WrappedComponent)  return WithFreeMoney } export default useFreeMoney 

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

الانحرافات عن المعايير المقبولة عمومًا تجعل من الصعب فهم كود شخص آخر.

4. تجنب الفخاخ المنطقية


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

افترض أننا أنشأنا مكونًا Typography يمكنه قبول الخيارات التالية: 'h1' ، 'h2' ، 'h3' ، 'h4' ، 'h5 ' ، 'h6' ، 'title' ، 'subheading' .

ما الذي سيؤثر بالضبط على ناتج المكون إذا تم تمرير الخيارات إليه في النموذج التالي؟

 const App = () => (  <Typography color="primary" align="center" subheading title>    Welcome to my bio  </Typography> ) 

أولئك الذين لديهم بعض الخبرة في React (أو بالأحرى مع JavaScript) قد يفترضون بالفعل أن خيار title subheading خيار subheading نظرًا لطريقة عمل النظام. سوف الخيار الأخير الكتابة فوق الأول.

لكن المشكلة هنا هي أنه لا يمكننا ، دون النظر إلى الكود ، أن نقول بالضبط إلى أي مدى سيتم تطبيق خيار title أو خيار title subheading .

على سبيل المثال:

 .title {  font-size: 1.2rem;  font-weight: 500;  text-transform: uppercase; } .subheading {  font-size: 1.1rem;  font-weight: 400;  text-transform: none !important; } 

على الرغم من فوز title ، فإن text-transform: uppercase CSS text-transform: uppercase لن يتم تطبيق قاعدة text-transform: uppercase . هذا بسبب خصوصية text-transform: none !important قاعدة text-transform: none !important موجودة في subheading . إذا لم تمارس الحذر في مثل هذه الحالات ، فقد يصبح تصحيح هذه الأخطاء في الأنماط أمرًا بالغ الصعوبة. خاصة - في الحالات التي لا يعرض فيها الكود بعض التحذيرات أو رسائل الخطأ في وحدة التحكم. هذا يمكن أن يعقد توقيع المكون.

أحد الحلول الممكنة لهذه المشكلة هو استخدام نسخة أنظف من مكون Typography :

 const App = () => <Typography variant="title">Welcome to my bio</Typography> 

هنا هو رمز مكون Typography :

 import React from 'react' import cx from 'classnames' import styles from './styles.css' const Typography = ({  children,  color = '#333',  align = 'left',  variant,  ...otherProps }) => {  return (    <div      className={cx({        [styles.h1]: variant === 'h1',        [styles.h2]: variant === 'h2',        [styles.h3]: variant === 'h3',        [styles.h4]: variant === 'h4',        [styles.h5]: variant === 'h5',        [styles.h6]: variant === 'h6',        [styles.title]: variant === 'title',        [styles.subheading]: variant === 'subheading',      })}    >      {children}    </div>  ) } 

الآن ، عندما ننتقل إلى مكون App إلى Typography component variant="title" ، يمكننا أن نتأكد من أن title فقط هو الذي سيؤثر على إخراج المكون. هذا يوفر علينا من الاضطرار إلى تحليل رمز المكون من أجل فهم كيف سيبدو هذا المكون.

للعمل مع الخصائص ، يمكنك استخدام التصميم البسيط if/else :

 let result if (variant === 'h1') result = styles.h1 else if (variant === 'h2') result = styles.h2 else if (variant === 'h3') result = styles.h3 else if (variant === 'h4') result = styles.h4 else if (variant === 'h5') result = styles.h5 else if (variant === 'h6') result = styles.h6 else if (variant === 'title') result = styles.title else if (variant === 'subheading') result = styles.subheading 

لكن القوة الرئيسية لهذا النهج هي أنه يمكنك ببساطة استخدام التصميم الفريد النظيف التالي ووضع حد له:

 const result = styles[variant] 

5. استخدام وظائف السهم


تمثل وظائف السهم آلية موجزة وواضحة لإعلان الوظائف في جافا سكريبت (في هذه الحالة ، سيكون من الأصح التحدث عن ميزة وظائف السهم على التعبيرات الوظيفية).

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

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

 //     function Gallery({ title, images = [], ...otherProps }) {  return (    <CarouselContext.Provider>      <Carousel>        {images.map((src, index) => (          <img align="center" src={src} key={`img_${index}`} />        ))}      </Carousel>    </CarouselContext.Provider>  ) } //         const Gallery = ({ title, images = [], ...otherProps }) => (  <CarouselContext.Provider>    <Carousel>      {images.map((src, index) => (        <img align="center" src={src} key={`img_${index}`} />      ))}    </Carousel>  </CarouselContext.Provider> ) 

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

 //     function GalleryPage(props) {  return <Gallery {...props} /> } //         const GalleryPage = (props) => <Gallery {...props} /> 

أنا متأكد من أن مثل هذه التصميمات أحادية الخط ستجذب الجميع.

6. ضع وظائف مستقلة خارج السنانير الخاصة بك


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

هنا مثال:

 import React from 'react' const initialState = {  initiated: false,  images: [], } const reducer = (state, action) => {  switch (action.type) {    case 'initiated':      return { ...state, initiated: true }    case 'set-images':      return { ...state, images: action.images }    default:      return state  } } const usePhotosList = ({ imagesList = [] }) => {  const [state, dispatch] = React.useReducer(reducer, initialState)  const removeFalseyImages = (images = []) =>    images.reduce((acc, img) => (img ? [...acc, img] : acc), [])  React.useEffect(() => {    const images = removeFalseyImages(imagesList)    dispatch({ type: 'initiated' })    dispatch({ type: 'set-images', images })  }, [])  return {    ...state,  } } export default usePhotosList 

إذا قمنا بتحليل هذا الرمز ، يمكننا أن نفهم أن وظائف removeFalseyImages ، في الواقع ، لا يجب أن تكون موجودة داخل الخطاف ؛ فهي لا تتفاعل مع حالتها ، مما يعني أنه يمكن وضعها خارجها ويمكن استدعاؤها من الخطاف دون أي مشاكل.

7. أن تكون متسقة عند كتابة التعليمات البرمجية


يعد الأسلوب الثابت لكتابة التعليمات البرمجية أمرًا موصى به غالبًا لأولئك الذين يبرمجون في JavaScript.

في حالة React ، يجدر الانتباه إلى نهج ثابت لاستخدام التصميمات التالية:

  1. فرق الاستيراد والتصدير.
  2. تسمية المكونات ، والسنانير ، ومكونات النظام العالي ، والطبقات.

عند استيراد المكونات وتصديرها ، أحيانًا أستخدم شيئًا مشابهًا لما يلي:

 import App from './App' export { default as Breadcrumb } from './Breadcrumb' export default App 

لكني أحب بناء الجملة أيضًا:

 export { default } from './App' export { default as Breadcrumb } from './Breadcrumb' 

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

من المهم للغاية الالتزام بتسمية اصطلاحات الكيانات.

على سبيل المثال ، إذا أعطى شخص ما useApp اسم useApp ، فمن المهم أن يتم إنشاء أسماء الخطافات الأخرى بطريقة مماثلة - باستخدام بادئة الاستخدام. على سبيل المثال ، قد يبدو اسم ربط آخر مع هذا الأسلوب useController .

إذا لم تلتزم بهذه القاعدة ، فقد يتحول رمز المشروع في النهاية إلى شيء من هذا القبيل:

 //  #1 const useApp = ({ data: dataProp = null }) => {  const [data, setData] = React.useState(dataProp)  React.useEffect(() => {    setData(data)  }, [])  return {    data,  } } //  #2 const basicController = ({ device: deviceProp }) => {  const [device, setDevice] = React.useState(deviceProp)  React.useEffect(() => {    if (!device && deviceProp) {      setDevice(deviceProp === 'mobile' ? 'mobile' : 'desktop')    }  }, [deviceProp])  return {    device,  } } 

إليك ما يبدو عليه استيراد هذه السنانير:

 import React from 'react' import useApp from './useApp' import basicController from './basicController' const App = () => {  const app = useApp()  const controller = basicController()  return (    <div>      {controller.errors.map((errorMsg) => (        <div>{errorMsg}</div>      ))}    </div>  ) } export default App 

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

 const app = useApp() const controller = useBasicController() 

أن تستمر ...

أعزائي القراء! كيف يمكنك التعامل مع تسمية الكيان في مشاريع React الخاصة بك؟

Source: https://habr.com/ru/post/ar465811/


All Articles