Руководство по написанию React/JSX кода от Airbnb
Наиболее разумный подход к написанию React и JSX
Это руководство основано на стандартах, которые в настоящее время преобладают в JavaScript сообществе, хотя некоторые соглашения (например, async/await или статические поля класса) могут все ещё быть включены или запрещены в каждом отдельном случае. В настоящее время что-либо, что не дошло до 3-ей стадии (stage 3) стандарта, не включено и не рекомендуется в этом руководстве.
Оглавление
- Основные правила
- Class против
React.createClass
против компонента без состояния (stateless) - Примеси (mixins)
- Именование
- Объявление
- Выравнивание
- Кавычки
- Пробелы
- Свойства (Props)
- Ссылки (Refs)
- Круглые скобки
- Теги
- Методы
- Последовательность
isMounted
- Переводы
Основные правила
- Включайте только один React компонент в файл.
- Однако, разрешается несколько компонентов без состояний (чистых) в файле. eslint:
react/no-multi-comp
.
- Однако, разрешается несколько компонентов без состояний (чистых) в файле. eslint:
- Всегда используйте JSX синтаксис.
- Не используйте
React.createElement
, если вы только не инициализируете программу из файла, который не является JSX. react/forbid-prop-types
разрешитarrays
иobjects
только, если явно указано, что содержатarray
иobject
, используяarrayOf
,objectOf
илиshape
.
Class против React.createClass
против компонента без состояния (stateless)
-
Если у вас есть внутреннее состояние (
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>; }
Примеси (mixins)
-
Почему? Примеси вносят неявные зависимости, становятся причиной конфликтов имён и быстрого роста сложности. Для большинства случаев, в которых используются примеси, можно более эффективно применить компоненты, компоненты высшего порядка или вспомогательные модули.
Именование
- Расширения: Используйте расширение
.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= /> // хорошо <Foo style= />
Пробелы
-
Всегда вставляйте один пробел в ваш самозакрывающийся тег. eslint:
no-multi-spaces
,react/jsx-tag-spacing
// плохо <Foo/> // очень плохо <Foo /> // плохо <Foo /> // хорошо <Foo />
-
Не отделяйте фигурные скобки пробелами в JSX. eslint:
react/jsx-curly-spacing
// плохо <Foo bar={ baz } /> // хорошо <Foo bar={baz} />
Свойства (Props)
-
Всегда используйте
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} /> }
Ссылки (Refs)
-
Всегда используйте функции обратного вызова. 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
:
- произвольные
static
методы constructor
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
- обработчики событий, начинающие с ‘handle’, такие как
handleSubmit()
илиhandleChangeDescription()
- обработчики событий, начинающие с ‘on’, такие как
onClickSubmit()
илиonChangeDescription()
- getter методы для
render
, такие какgetSelectReason()
илиgetFooterContent()
- произвольные render методы, такие как
renderNavigation()
илиrenderProfilePicture()
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
displayName
propTypes
contextTypes
childContextTypes
mixins
statics
defaultProps
getDefaultProps
getInitialState
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
- обработчики кликов или событий, такие как
onClickSubmit()
илиonChangeDescription()
- getter методы для
render
, такие какgetSelectReason()
илиgetFooterContent()
- произвольные render методы, такие как
renderNavigation()
илиrenderProfilePicture()
render
isMounted
-
Не используйте
isMounted
. eslint:react/no-is-mounted
Почему?
isMounted
— это антипаттерн, который недоступен при использовании ES6 классов и который планируют официально признать устаревшим.
Переводы
Это JSX/React руководство также доступно и на других языках:
- Китайский (Упрощённый): jhcccc/javascript
- Китайский (Традиционный): jigsawye/javascript
- Испанский: agrcrobles/javascript
- Японский: mitsuruog/javascript-style-guide
- Корейский: apple77y/javascript
- Польский: pietraszekl/javascript
- Португальский: ronal2do/javascript
- Русский: leonidlebedev/javascript-airbnb
- Тайский: lvarayut/javascript-style-guide
- Турецкий: alioguzhan/react-style-guide
- Украинский: ivanzusko/javascript
- Вьетнамский: uetcodecamp/jsx-style-guide