Skip to content

Latest commit

 

History

History
759 lines (586 loc) · 32.5 KB

README.md

File metadata and controls

759 lines (586 loc) · 32.5 KB

Руководство по написанию React/JSX кода от Airbnb

Наиболее разумный подход к написанию React и JSX

Это руководство основано на стандартах, которые в настоящее время преобладают в JavaScript сообществе, хотя некоторые соглашения (например, async/await или статические поля класса) могут все ещё быть включены или запрещены в каждом отдельном случае. В настоящее время что-либо, что не дошло до 3-ей стадии (stage 3) стандарта, не включено и не рекомендуется в этом руководстве.

Оглавление

  1. Основные правила
  2. Class против React.createClass против компонента без состояния (stateless)
  3. Примеси (mixins)
  4. Именование
  5. Объявление
  6. Выравнивание
  7. Кавычки
  8. Пробелы
  9. Свойства (Props)
  10. Ссылки (Refs)
  11. Круглые скобки
  12. Теги
  13. Методы
  14. Последовательность
  15. isMounted
  16. Переводы
  • Включайте только один React компонент в файл.
  • Всегда используйте JSX синтаксис.
  • Не используйте React.createElement, если вы только не инициализируете программу из файла, который не является JSX.
  • react/forbid-prop-types разрешит arrays и objects только, если явно указано, что содержат array и object, используя arrayOf, objectOf или shape.
  • Если у вас есть внутреннее состояние (state) и/или ссылки (refs), отдавайте предпочтение class extends React.Component вместо React.createClass. eslint: react/prefer-es6-class react/prefer-stateless-function

    // плохо
    const Listing = React.createClass({
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    });
    
    // хорошо
    class Listing extends React.Component {
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    }

    И если у вас нет состояния (state) или ссылок (refs), отдавайте предпочтение нормальным функциям (не стрелочным) над классами:

    // плохо
    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>;
    }
  • Не используйте примеси.

    Почему? Примеси вносят неявные зависимости, становятся причиной конфликтов имён и быстрого роста сложности. Для большинства случаев, в которых используются примеси, можно более эффективно применить компоненты, компоненты высшего порядка или вспомогательные модули.

  • Расширения: Используйте расширение .jsx для компонентов React. eslint: react/jsx-filename-extension

  • Имя файла: Используйте PascalCase для названий файлов, например, ReservationCard.jsx.

  • Именование переменной: Используйте PascalCase для компонентов React и camelCase для их экземпляров. eslint: react/jsx-pascal-case

    // плохо
    import reservationCard from './ReservationCard';
    
    // хорошо
    import ReservationCard from './ReservationCard';
    
    // плохо
    const ReservationItem = <ReservationCard />;
    
    // хорошо
    const reservationItem = <ReservationCard />;
  • Именование компонента: Называйте файлы так же как и компоненты. Например, ReservationCard.jsx должен содержать внутри компонент ReservationCard. Однако корневые компоненты в директории должны лежать в файле index.jsx, и в этом случае название папки должно быть таким же, как название компонента:

    // плохо
    import Footer from './Footer/Footer';
    
    // плохо
    import Footer from './Footer/index';
    
    // хорошо
    import Footer from './Footer';
  • Именование компонента высшего порядка: Используйте сочетание имени компонента высшего порядка и имени переданного в него компонента как свойство 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;
    }
  • Названия свойств: Избегайте использования названий свойств DOM-компонента для других целей.

    Почему? Люди ожидают, что такие свойства как style и className имеют одно определённое значение. Изменение этого API в вашем приложении ухудшает читабельность и поддержку кода, что может приводить к ошибкам.

    // плохо
    <MyComponent style="fancy" />
    
    // плохо
    <MyComponent className="fancy" />
    
    // хорошо
    <MyComponent variant="fancy" />
  • Не используйте свойство displayName для именования компонентов. Вместо этого задавайте имя классу компонента.

    // плохо
    export default React.createClass({
      displayName: 'ReservationCard',
      // здесь начинается работа
    });
    
    // хорошо
    export default class ReservationCard extends React.Component {
    }
  • Следуйте приведённым ниже стилям для JSX-синтаксиса. eslint: react/jsx-closing-bracket-location

    // плохо
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // хорошо
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // если свойства помещаются на одну строку, оставляйте их на одной строке
    <Foo bar="bar" />
    
    // отступ у дочерних элементов задается как обычно
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    
    // плохо
    {showButton &&
      <Button />
    }
    
    // плохо
    {
      showButton &&
        <Button />
    }
    
    // хорошо
    {showButton && (
      <Button />
    )}
    
    // хорошо
    {showButton && <Button />}
    
    // хорошо
    {someReallyLongConditional
      && anotherLongConditional
      && (
        <Foo
          superLongParam="bar"
          anotherSuperLongParam="baz"
        />
      )
    }
    
    // хорошо
    {someConditional ? (
      <Foo />
    ) : (
      <Foo
        superLongParam="bar"
        anotherSuperLongParam="baz"
      />
    )}
  • Всегда используйте двойные кавычки (") для JSX-атрибутов, а одинарные кавычки (') для всего остального JS. eslint: jsx-quotes

    Почему? Для стандартных HTML-атрибутов обычно используются двойные кавычки, а не одинарные, и JSX-атрибуты тоже следуют этому соглашению.

    // плохо
    <Foo bar='bar' />
    
    // хорошо
    <Foo bar="bar" />
    
    // плохо
    <Foo style={{ left: "20px" }} />
    
    // хорошо
    <Foo style={{ left: '20px' }} />
  • Всегда вставляйте один пробел в ваш самозакрывающийся тег. eslint: no-multi-spaces, react/jsx-tag-spacing

    // плохо
    <Foo/>
    
    // очень плохо
    <Foo                 />
    
    // плохо
    <Foo
     />
    
    // хорошо
    <Foo />
  • Не отделяйте фигурные скобки пробелами в JSX. eslint: react/jsx-curly-spacing

    // плохо
    <Foo bar={ baz } />
    
    // хорошо
    <Foo bar={baz} />
  • Всегда используйте camelCase для названий свойств или PascalCase, если значение является React-компонентом.

    // плохо
    <Foo
      UserName="hello"
      phone_number={12345678}
    />
    
    // хорошо
    <Foo
      userName="hello"
      phoneNumber={12345678}
      Component={SomeComponent}
    />
  • Не указывайте значение свойства, когда оно явно true. eslint: react/jsx-boolean-value

    // плохо
    <Foo
      hidden={true}
    />
    
    // хорошо
    <Foo
      hidden
    />
    
    // хорошо
    <Foo hidden />
  • Всегда добавляйте свойство alt для тегов <img>. Если изображение является презентационным, alt может быть пустой строкой или <img> обязан иметь role="presentation". eslint: jsx-a11y/alt-text

    // плохо
    <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>. eslint: jsx-a11y/img-redundant-alt

    Почему? Скринридеры уже сообщают что img элементы являются картинками, так что нет необходимости включать эту информацию в текст свойства alt.

    // плохо
    <img src="hello.jpg" alt="Picture of me waving hello" />
    
    // хорошо
    <img src="hello.jpg" alt="Me waving hello" />
  • Используйте только валидные, не абстрактные ARIA роли. eslint: jsx-a11y/aria-role

    // плохо - не ARIA роль
    <div role="datepicker" />
    
    // плохо - асбтрактная ARIA роль
    <div role="range" />
    
    // хорошо
    <div role="button" />
  • Не используйте accessKey на элементах. eslint: jsx-a11y/no-access-key

    Почему? Несоответствия между сочетанием комбинаций клавиш и командами с клавиатуры затрудняют доступ для людей, которые пользуются экранными считывателями и клавиатурами.

    // плохо
    <div accessKey="h" />
    
    // хорошо
    <div />
  • Не используйте индексы элементов массива в качестве свойства key. Отдавайте предпочтение уникальному ID. eslint: react/no-array-index-key

    Почему? Неиспользование стабильного ID является антипаттерном, потому что это может негативно повлиять на производительность компонента и вызвать проблемы с его состоянием.

    Мы не рекомендуем использовать индексы для ключей, если порядок элементов может измениться.

    // плохо
    {todos.map((todo, index) =>
      <Todo
        {...todo}
        key={index}
      />
    )}
    
    // хорошо
    {todos.map(todo => (
      <Todo
        {...todo}
        key={todo.id}
      />
    ))}
  • Всегда указывайте подробные defaultProps для всех свойств, которые не указаны как необходимые.

    Почему? propTypes является способом документации, а предоставление 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,
    };
  • Используйте оператор расширения для свойств осознанно.

    Почему? В противном случае вы скорее всего будете передавать внутрь компонента лишние свойства. А для React версии 15.6.1 и старше, вы можете передать невалидные HTML-атрибуты в DOM.

    Исключения:

    • Компоненты высшего порядка, которые передают свойства внутрь дочернего компонента и поднимают propTypes.
    function HOC(WrappedComponent) {
      return class Proxy extends React.Component {
        Proxy.propTypes = {
          text: PropTypes.string,
          isLoading: PropTypes.bool
        };
    
        render() {
          return <WrappedComponent {...this.props} />
        }
      }
    }
    • Использование оператора расширения для известных, явно заданных свойств. Это может быть особенно полезно при тестировании компонентов React с конструкцией beforeEach из Mocha.
    export default function Foo {
      const props = {
        text: '',
        isPublished: false
      }
    
      return <div {...props} />;
    }

    Примечания по использованию: Если возможно, отфильтруйте ненужные свойства. Кроме того, используйте prop-types-exact, чтобы предотвратить ошибки.

    // плохо
    render() {
      const { irrelevantProp, ...relevantProps } = this.props;
      return <WrappedComponent {...this.props} />
    }
    
    // хорошо
    render() {
      const { irrelevantProp, ...relevantProps } = this.props;
      return <WrappedComponent {...relevantProps} />
    }
  • Всегда используйте функции обратного вызова. eslint: react/no-string-refs

    // плохо
    <Foo
      ref="myRef"
    />
    
    // хорошо
    <Foo
      ref={(ref) => { this.myRef = ref; }}
    />
  • Оборачивайте в скобки JSX теги, когда они занимают больше одной строки. eslint: 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>;
    }
  • Всегда используйте самозакрывающиеся теги, если у элемента нет дочерних элементов. eslint: react/self-closing-comp

    // плохо
    <Foo variant="stuff"></Foo>
    
    // хорошо
    <Foo variant="stuff" />
  • Если ваш компонент имеет множество свойств, которые располагаются на нескольких строчках, то закрывайте тег на новой строке. eslint: react/jsx-closing-bracket-location react/jsx-closing-tag-location

    // плохо
    <Foo
      bar="bar"
      baz="baz" />
    
    // хорошо
    <Foo
      bar="bar"
      baz="baz"
    />
  • Используйте стрелочные функции для замыкания локальных переменных. Это удобно, когда вам нужно передать дополнительные данные в обработчик событий. Однако, убедитесь, что они не сильно вредят производительности. В частности, могут быть ненужные перерисовки каждый раз, когда они будут передаваться PureComponent.

    function ItemList(props) {
      return (
        <ul>
          {props.items.map((item, index) => (
            <Item
              key={item.key}
              onClick={(event) => { doSomethingWith(event, item.name, index); }}
            />
          ))}
        </ul>
      );
    }
  • Привязывайте обработчики событий для метода render в конструкторе. eslint: react/jsx-no-bind

    Почему? Вызов bind в методе render создаёт новую функцию при каждой перерисовке. Не используйте стрелочные функции в полях класса, поскольку это делает их сложными для тестирования и отладки, и может негативно повлиять на производительность. К тому же концептуально поля класса предназначены для данных, а не для логики.

    // плохо
    class extends React.Component {
      onClickDiv() {
        // ...
      }
    
      render() {
        return <div onClick={this.onClickDiv.bind(this)} />;
      }
    }
    
    // очень плохо
    class extends React.Component {
      onClickDiv = () => {
        // ...
      }
      render() {
        return <div onClick={this.onClickDiv} />
      }
    }
    
    // хорошо
    class extends React.Component {
      constructor(props) {
        super(props);
    
        this.onClickDiv = this.onClickDiv.bind(this);
      }
    
      onClickDiv() {
        // ...
      }
    
      render() {
        return <div onClick={this.onClickDiv} />;
      }
    }
  • Не используйте префикс подчёркивания для именования внутренних методов React компонента.

    Почему? Префиксы подчёркивания иногда используются в других языках как соглашение о приватности. Но, в отличие от этих языков, в Javascript нет нативной поддержки приватности, все публично. Независимо от ваших намерений, добавление префикса подчёркивания к вашим свойства не делает их приватными, и любые свойства (с подчёркиванием или без) должны рассматриваться как публичные. Смотрите вопросы #1024, и #490 для более глубокого обсуждения.

    // плохо
    React.createClass({
      _onClickSubmit() {
        // ...
      },
    
      // ...
    });
    
    // хорошо
    class extends React.Component {
      onClickSubmit() {
        // ...
      }
    
      // ...
    }
  • Всегда возвращайте значение в методах render. eslint: react/require-render-return

    // плохо
    render() {
      (<div />);
    }
    
    // хорошо
    render() {
      return (<div />);
    }
  • Последовательность для 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. обработчики событий, начинающие с 'handle', такие как handleSubmit() или handleChangeDescription()
  12. обработчики событий, начинающие с 'on', такие как onClickSubmit() или onChangeDescription()
  13. getter методы для render, такие как getSelectReason() или getFooterContent()
  14. произвольные render методы, такие как renderNavigation() или renderProfilePicture()
  15. 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: eslint: 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. getter методы для render, такие как getSelectReason() или getFooterContent()
  20. произвольные render методы, такие как renderNavigation() или renderProfilePicture()
  21. render

isMounted

  • Не используйте isMounted. eslint: react/no-is-mounted

    Почему? isMounted — это антипаттерн, который недоступен при использовании ES6 классов и который планируют официально признать устаревшим.

Это JSX/React руководство также доступно и на других языках:

⬆ к оглавлению