دليل Airbnb لتنسيق شيفرات React/JSX


جميل بيلوني

القواعد الأساسية

  • لا تضع إلا مكوِّن React واحدًا فقط في كل ملف.
    • مع ذلك، يُسمح بوضع أكثر من مكوّن من الدوال عديمة الحالة (Stateless functions) في ملف واحد. استعن بقاعدة react/no-multi-comp في ESLint.
  • استخدم دائمًا أسلوب الصياغة JSX.
  • لا تستخدم التابع React.createElement إلا إذا كنت تهيئ التطبيق من ملف لا يستخدم صياغة JSX.

Class مقابل React.createClass مقابل stateless

  • استخدم class extends React.Component بدلًا من React.createClass إذا كانت لديك حالة أو مراجع (Refs) داخلية أو هما معًا.

    // سيّئ
    const Listing = React.createClass({
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    });
    
    // جيّد
    class Listing extends React.Component {
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    }

     

  • أما إذا لم تكن لديك حالة داخلية أو مرجعية، فمن الأفضل استخدام الدوال العادية (وليس الدوال السهمية) بدلًا من استخدام الأصناف.

// سيّئ
class Listing extends React.Component {
  render() {
    return <div>{this.props.hello}</div>;
  }
}

// سيّئ (يُنصَح بعدم الاعتماد على استنباط اسم الدالة)

const Listing = ({ hello }) => (
  <div>{hello}</div>
);

// جيّد
function Listing({ hello }) {
  return <div>{hello}</div>;
}

الخلائط (Mixins)

  • تجنب استخدام الخلائط.
    • لماذا؟ لأنها تنطوي على اعتمادات (Dependencies) ضمنية، كما قد تتسبب في اشتباك الأسماء، وترفع درجة التعقيد. يمكن استبدال الخلائط في معظم الحالات بطرق أفضل عبر المكوّنات (Components)، أو المكوّنات ذات المستوى العالي (Higher-order components) أو الوحدات المساعدة.

التسمية

  • الامتدادات: استخدم الامتداد jsx. لمكوّنات React .
  • اسم الملف: استخدم أسلوب التسمية PascalCase لأسماء الملفات. على سبيل المثال، ReservationCard.jsx.
  • تسمية المراجع: استخدم أسلوب التسمية PascalCase لمكوّنات React وأسلوب camelCase لنسخ الكائنات (Instances).
// سيّئ
import reservationCard from './ReservationCard';

// جيّد
import ReservationCard from './ReservationCard';

// سيّئ
const ReservationItem = <ReservationCard />;

// جيّد
const reservationItem = <ReservationCard />;
  • تسمية المكوّنات: استخدم اسم الملف كاسم للمكوّن. على سبيل المثال، ReservationCard.jsx ينبغي أن يكون اسم مرجعها ReservationCard. بالنسبة للمكوّنات الجذرية للمجلد (Root components)، استخدم index.jsx لاسم للملف واستخدم اسم المجلد لاسم المكوّن:
// سيّئ
import Footer from './Footer/Footer';

// سيّئ
import Footer from './Footer/index';

// جيّد
import Footer from './Footer';
  • تسمية المكوّنات ذات المستوى العالي (Higher-order Componen): استخدم مزيجًا من اسم المكوّن ذي المستوى العالي واسم المكوِّن المُمرَّر ليكون قيمة الخاصيّة displayName في المكوّن المُولَّد.على سبيل المثال، إذا مُرّر للمكوّن ذي المستوى العالي ()withFoo المكوّن Bar فإن الناتج ستكون قيمة الخاصيّة displayName لديه هي withFoo(Bar).

    لماذا ا؟ يمكن استخدام الخاصيّةdisplayName في أدوات المطوّرين أو في رسائل الخطأ، وعندما تعكس قيمتها تلك العلاقة بوضوح، فسيساعد ذلك على فهم ما يحدث.

// سيّئ
export default function withFoo(WrappedComponent) {
  return function WithFoo(props) {
    return <WrappedComponent {...props} foo />;
  }
}

// جيّد
export default function withFoo(WrappedComponent) {
  function WithFoo(props) {
    return <WrappedComponent {...props} foo />;
  }

  const wrappedComponentName = WrappedComponent.displayName
    || WrappedComponent.name
    || 'Component';

  WithFoo.displayName = `withFoo(${wrappedComponentName})`;
  return WithFoo;
}
  • تسمية الخاصيّات (Props) : تجنب استعمال أسماء مكوّنات DOM لأغراض مختلفة. لماذا؟ يتوقع المطوّرون أنّ خاصيّات مثل style وclassName تعني أشياء محددة. تغيير الواجهة البرمجية هذه في جزء من تطبيقك يجعل الشفرة البرمجية أقل قابلية للقراءة والصيانة، ويمكن أن يتسبب في أعطاب.

    // سيّئ
    <MyComponent style="fancy" />
    
    // جيّد
    <MyComponent className="fancy" />
    
    // جيّد
    <MyComponent variant="fancy" />

     

التصريح Declaration

  • لا تستخدم displayName لتسمية المكوّنات. بدلًا من ذلك، سمّ المكوّنات بمراجعها.

    // سيّئ
    export default React.createClass({
      displayName: 'ReservationCard',
      // stuff goes here
    });
    
    // جيّد
    export default class ReservationCard extends React.Component {
    }

     

المحاذاة Alignment

  • اتبع الأساليب التالية للمحاذاة في صياغة JSX. استعن بقاعدتيْ react/jsx-closing-bracket-location وreact/jsx-closing-tag-location في ESLint.

    // سيّئ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // جيّد
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // if props fit in one line then keep it on the same line
    <Foo bar="bar" />
    
    // children get indented normally
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>

     

الاقتباس Quotes

  • استخدم دائمًا علامات الاقتباس المزدوجة (") لخاصيّات JSX، وعلامات الاقتباس المفردة (') لبقية عناصر جافاسكريبت. استعن بقاعدة jsx-quotes في ESLint.

    لماذا؟ تستخدم خاصيّات HTML عادةً علامات الاقتباس المزدوجة بدلًا من المفردة، لذا فخاصيّات JSX تتبع هذا الاصطلاح.

    // سيّئ
    <Foo bar='bar' />
    
    // جيّد
    <Foo bar="bar" />
    
    // سيّئ
    <Foo style={{ left: "20px" }} />
    
    // جيّد
    <Foo style={{ left: '20px' }} />

     

إدراج المسافات Spacing

  • أضف دائمًا مسافةً واحدةً في الوسوم المنغلقة على ذاتها (Self-closing tags). استعن بالقاعدتين no-multi-spaces وreact/jsx-tag-spacing.

    // سيّئ
    <Foo/>
    
    // سيّئ جدًّا
    <Foo                 />
    
    // سيّئ
    <Foo
     />
    
    // جيّد
    <Foo />

     

  • لا تحش أقواس JSX المعقوصة بمسافات. استعن بالقاعدة react/jsx-curly-spacing.

    // سيّئ
    <Foo bar={ baz } />
    
    // جيّد
    <Foo bar={baz} />

     

الخاصيات Props

  • استخدم دائمًا أسلوب التسمية camelCase لتسمية الخاصيّات.

    // سيّئ
    <Foo
      UserName="hello"
      phone_number={12345678}
    />
    
    // جيّد
    <Foo
      userName="hello"
      phoneNumber={12345678}
    />

     

  • احذف قيمة الخاصيّة عندما تكون قيمتها تساوي true على نحو صريح. استعن بالقاعدة react/jsx-boolean-value.

    // سيّئ
    <Foo
      hidden={true}
    />
    
    // جيّد
    <Foo
      hidden
    />
    
    // جيّد
    <Foo hidden />

     

  • أضف دومًا الخاصيّة alt في وسوم الصور(<img>). إذا كانت الصورة تقديمية (Presentational)، فيمكن للخاصيّة alt أن تكون نصًّا فارغًا وإلّا فيجب أن يحتوي الوسم <img> على الخاصيّة ‎role="presentation".

    // سيّئ
    <img src="hello.jpg" />
    
    // جيّد
    <img src="hello.jpg" alt="Me waving hello" />
    
    // جيّد
    <img src="hello.jpg" alt="" />
    
    // جيّد
    <img src="hello.jpg" role="presentation" />

     

  • لا تستخدم كلمات مثل "image" أو "photo " أو "picture" أو "صورة" في خاصيّات alt الخاصة بوسوم <img>. استعن بالقاعدة jsx-a11y/img-redundant-alt.

    لماذا؟ تعلم برامج قراءة الشاشة أن <img> تعني صورة، لذلك لا توجد حاجة لإدراج هذه المعلومة في النص البديل (alt text).

    // سيّئ
    <img src="hello.jpg" alt="صورة مني وأنا أشير بيدي للترحيب" />
    
    // جيّد
    <img src="hello.jpg" alt="أشير بيدي للترحيب" />

     

  • لا تستخدم إلّا أدوار ARIA الصالحة وغير المجردة. استعن بالقاعدة jsx-a11y/aria-role.

    // سيّئ، ليس من أدوار ARIA
    <div role="datepicker" />
    
    // سيّئ، دور ARIA مجرّد
    <div role="range" />
    
    // جيّد
    <div role="button" />

     

  • لا تستخدم الخاصيّة accesskey على العناصر. استعن بالقاعدة jsx-a11y/no-access-key.

    لماذا؟ تعقّد التناقضات بين اختصارات لوحة المفاتيح وأوامر لوحة المفاتيح التي يستعملها مَن يستخدمون برامج قراءة الشاشة ولوحة المفاتيح، تعقّد قابلية الوصول (Accessibility).

    // سيّئ
    <div accessKey="h" />
    
    // جيّد
    <div />

     

  • تجنب استخدام فهرس مصفوفة (Array index) ليكون خاصيّة key. استخدم معرّفًا فريدًا بدلًا من ذلك. (لماذا؟).

    // سيّئ
    {todos.map((todo, index) =>
      <Todo
        {...todo}
        key={index}
      />
    )}
    
    // جيّد
    {todos.map(todo => (
      <Todo
        {...todo}
        key={todo.id}
      />
    ))}
    • عرّف دائمًا قيمًا افتراضيّة (defaultProps) للخاصيّات غير المطلوبة.

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

    // سيّئ
    function SFC({ foo, bar, children }) {
      return <div>{foo}{bar}{children}</div>;
    }
    SFC.propTypes = {
      foo: PropTypes.number.isRequired,
      bar: PropTypes.string,
      children: PropTypes.node,
    };
    
    // جيّد
    function SFC({ foo, bar, children }) {
      return <div>{foo}{bar}{children}</div>;
    }
    SFC.propTypes = {
      foo: PropTypes.number.isRequired,
      bar: PropTypes.string,
      children: PropTypes.node,
    };
    SFC.defaultProps = {
      bar: '',
      children: null,
    };

     

  • لا تُسرف في استخدام الخاصيّات الممدَّدة (Spread props).

لماذا؟ لكي تتجنب قدر الإمكان تمرير الخاصيّات التي لا داعي لها إلى المكوّنات. بالنسبة للإصدار React v15.6.1 والإصدارت الأقدم، ستتجنب كذلك تمرير سمات HTML غير صالحة إلى نموذج DOM.

استثناءات:

  • المكوّنات ذات المستوى العالي التي تغلّف الخاصيّات وترفع الخاصيّة propTypes إلى أعلى النطاق (Hoist):

    	function HOC(WrappedComponent) {
    	  return class Proxy extends React.Component {
    	    Proxy.propTypes = {
    	      text: PropTypes.string,
    	      isLoading: PropTypes.bool
    	    };
    	
    	    render() {
    	      return <WrappedComponent {...this.props} />
    	    }
    	  }
    	}
  • تمديد الكائنات بخاصيّات معروفة وواضحة يمكن أن يكون مفيدًا خصوصًا عند اختبار مكوّنات React بالتركيب forEach في Mocha.

    export default function Foo {
      const props = {
        text: '',
        isPublished: false
      }
    
      return (<div {...props} />);
    }

     

  • ملحوظة: حاول تصفية الخاصيّات التي لا داعي لها، واستخدم prop-types-exact لمساعدتك على تجنب العلل.

    // جيّد
    render() {
      const { irrelevantProp, ...relevantProps  } = this.props;
      return <WrappedComponent {...relevantProps} />
    }
    
    // سيّئ
    render() {
      const { irrelevantProp, ...relevantProps  } = this.props;
      return <WrappedComponent {...this.props} />
    }

     

المرجعيّات Refs

  • استخدم دائمًا رد نداء (Callback) للمراجع (Refs). استعن بالقاعدة react/no-string-refs.

    // سيّئ
    <Foo
      ref="myRef"
    />
    
    // جيّد
    <Foo
      ref={(ref) => { this.myRef = ref; }}
    />

     

الأقواس

  • ضع وسوم JSX بين أقواس إذا امتدت على أكثر من سطر واحد. استعن بالقاعدة react/jsx-wrap-multilines.

    // سيّئ
    render() {
      return <MyComponent variant="long body" foo="bar">
               <MyChild />
             </MyComponent>;
    }
    
    // جيّد
    render() {
      return (
        <MyComponent variant="long body" foo="bar">
          <MyChild />
        </MyComponent>
      );
    }
    
    // جيّد، بالنسبة لسطر واحد
    render() {
      const body = <div>hello</div>;
      return <MyComponent>{body}</MyComponent>;
    }

     

الوسوم Tags

  • استخدم دائمًا الإغلاق الذاتي (Self-close) للوسوم التي لا أبناء لها. استعن بالقاعدة react/self-closing-comp.

    // سيّئ
    <Foo variant="stuff"></Foo>
    
    // جيّد
    <Foo variant="stuff" />

     

  • اجعل إغلاق الوسوم في سطر جديد إذا كان للمكوّن خاصيّات متعددة الأسطر. استعن بالقاعدة react/jsx-closing-bracket-location.

    // سيّئ
    <Foo
      bar="bar"
      baz="baz" />
    
    // جيّد
    <Foo
      bar="bar"
      baz="baz"
    />

     

التوابع Methods

  • استخدم الدوال السهمية في المتغيرات المحلية.

    function ItemList(props) {
      return (
        <ul>
          {props.items.map((item, index) => (
            <Item
              key={item.key}
              onClick={() => doSomethingWith(item.name, index)}
            />
          ))}
        </ul>
      );
    }

     

  • اربط معالجات الأحداث الخاصّة بتابع التصيير (Render methode) داخل المنشئ. استعن بالقاعدة react/jsx-no-bind.

    لماذا؟ استدعاء bind في مسار التصيير (Render path) يُنشئ دالة جديدة لكل تابع تصيير.

    	// سيّئ
    	class extends React.Component {
    	  onClickDiv() {
    	    // do stuff
    	  }
    	
    	  render() {
    	    return <div onClick={this.onClickDiv.bind(this)} />;
    	  }
    	}
    	
    	// جيّد
    	class extends React.Component {
    	  constructor(props) {
    	    super(props);
    	
    	    this.onClickDiv = this.onClickDiv.bind(this);
    	  }
    	
    	  onClickDiv() {
    	    // do stuff
    	  }
    	
    	  render() {
    	    return <div onClick={this.onClickDiv} />;
    	  }
    	}
  • لا تستخدم الشرطة السفلية (_) في بداية أسماء التوابع الداخلية لمكوّنات React .

    لماذا؟ تُستخدَم الشرطات السفلية في بداية أسماء المتغيّرات في لغات أخرى للدلالة على الخصوصية. ولكن، خلافًا لتلك اللغات، لا يوجد دعم أصيل للخصوصية في جافاسكريبت، فكل شيء فيها عام. إضافة شرطة سفلية في بداية أسماء الخاصيات، بغض النظرعن نواياك، لا يجعلها خاصة، ويجب أن تُعامَل كل الخاصيات (مسبوقة بالشرطة السفلية أو لا) على أنها عامة. انظر إلى النقاش حول 1024# وenter link description here للتعمّق.

    // سيّئ
    React.createClass({
      _onClickSubmit() {
        // do stuff
      },
    
      // other stuff
    });
    
    // جيّد
    class extends React.Component {
      onClickSubmit() {
        // do stuff
      }
    
      // other stuff
    }

     

  • تأكد من إرجاع قيمة في توابع التصيير. استعن بالقاعدة react/require-render-return.

    // سيّئ
    render() {
      (<div />);
    }
    
    // جيّد
    render() {
      return (<div />);
    }

     

الترتيب Ordering

  • الترتيب عند تمديد الصنف React.Component لإنشاء مكوّن React جديد (class extends React.Component):
  1. التوابع الثابتة (static) الاختيارية،
  2. constructor
  3. getChildContext
  4. componentWillMount
  5. componentDidMount
  6. componentWillReceiveProps
  7. shouldComponentUpdate
  8. componentWillUpdate
  9. componentDidUpdate
  10. componentWillUnmount
  11. معالجات الأحداث والنقر مثل ‎onClickSubmit‎()‎ أو ‎onChangeDescription‎()‎
  12. توابع الوصول الخاصة بـ render مثل getSelectReason()‎ أو getFooterContent()
  13. توابع render الاختيارية مثل renderNavigation()‎ أو renderProfilePicture()‎.
  14. render.
  • كيفية تعريف propTypes وdefaultProps وcontextTypes

    import React from 'react';
    import PropTypes from 'prop-types';
    
    const propTypes = {
      id: PropTypes.number.isRequired,
      url: PropTypes.string.isRequired,
      text: PropTypes.string,
    };
    
    const defaultProps = {
      text: 'Hello World',
    };
    
    class Link extends React.Component {
      static methodsAreOk() {
        return true;
      }
    
      render() {
        return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>;
      }
    }
    
    Link.propTypes = propTypes;
    Link.defaultProps = defaultProps;
    
    export default Link;

    • الترتيب الخاص بـ React.createClass. استعن بالقاعدة react/sort-comp.

  1. displayName
  2. propTypes
  3. contextTypes
  4. childContextTypes
  5. mixins
  6. statics
  7. defaultProps
  8. getDefaultProps
  9. getInitialState
  10. getChildContext
  11. componentWillMount
  12. componentDidMount
  13. componentWillReceiveProps
  14. shouldComponentUpdate
  15. componentWillUpdate
  16. componentDidUpdate
  17. componentWillUnmount
  18. معالجات الأحداث والنقر مثل ‎onClickSubmit‎()‎ أو ‎onChangeDescription‎()‎
  19. توابع الوصول الخاصة بـ render مثل getSelectReason()‎ أو getFooterContent()
  20. توابع render الاختيارية مثل renderNavigation()‎ أو renderProfilePicture()‎.
  21. render
  • لا تستخدم isMounted. استعن بالقاعدة react/no-is-mounted

    لماذا؟ استخدام isMounted غير مجدٍ (Anti-pattern)، وهو غير متوفر عند استخدام أصناف ES6، كما أنه سيُلغى رسميًّا قريبا.

ترجمة - وبتصرّف - للمقال Airbnb React/JSX Style Guide.





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن