Руководство по написанию React/JSX кода от Airbnb
Наиболее разумный подход к написанию React и JSX
- Основные правила
- Class против
React.createClassпротив компонента без состояния (stateless) - Примеси (mixins)
- Именование
- Объявление
- Выравнивание
- Кавычки
- Пробелы
- Свойства (Props)
- Ссылки (Refs)
- Круглые скобки
- Теги
- Методы
- Последовательность
isMounted- Переводы
- Включайте только один React компонент в файл.
- Однако, разрешается несколько компонентов без состояний (чистых) в файле. eslint:
react/no-multi-comp.
- Однако, разрешается несколько компонентов без состояний (чистых) в файле. eslint:
- Всегда используйте JSX синтаксис.
- Не используйте
React.createElement, если вы только не инициализируете программу из файла, который не является JSX.
-
Если у вас есть внутреннее состояние (
state) и/или ссылки (refs), отдавайте предпочтениеclass extends React.ComponentвместоReact.createClass. eslint:react/prefer-es6-classreact/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>; }
Почему? Примеcи вносят неявные зависимости, становятся причиной конфликтов имен и быстрого роста сложностей. Для большинства случаев, в которых используются примеси, можно более эффективно применить компоненты, компоненты высшего порядка или вспомогательные модули.
-
Расширения: Используйте расширение
.jsxдля компонентов React. -
Имя файла: Используйте
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может использоваться в инструментах разработчика или сообщениях об ошибках, и если оно ясно выражает связь между компонентами, это помогает понять, что происходит.
```jsx
// плохо
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 в вашем приложении ухудшает читабельность и поддержку кода, что может приводить к ошибкам.
```jsx
// плохо
<MyComponent style="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>
- Всегда используйте двойные кавычки (
") для JSX-атрибутов, а одинарные кавычки (') для всего остального JS. eslint:jsx-quotes
Почему? Для стандартных HTML-атрибутов обычно используются двойные кавычки, а не одинарные, и JSX-атрибуты тоже следуют этому соглашению.
```jsx
// плохо
<Foo bar='bar' />
// хорошо
<Foo bar="bar" />
// плохо
<Foo style={{ left: "20px" }} />
// хорошо
<Foo style={{ left: '20px' }} />
```
-
Всегда вставляйте один пробел в ваш самозакрывающийся тег. eslint:
no-multi-spaces,react/jsx-space-before-closing// плохо <Foo/> // very bad <Foo /> // плохо <Foo /> // хорошо <Foo />
-
Не отделяйте фигурные скобки пробелами в JSX. eslint:
react/jsx-curly-spacing// плохо <Foo bar={ baz } /> // хорошо <Foo bar={baz} />
-
Всегда используйте
camelCaseдля названий свойств.// плохо <Foo UserName="hello" phone_number={12345678} /> // хорошо <Foo userName="hello" phoneNumber={12345678} />
-
Не указывайте значение свойства, когда оно явно
true. eslint:react/jsx-boolean-value// плохо <Foo hidden={true} /> // хорошо <Foo hidden />
-
Всегда добавляйте свойство
altдля тегов<img>. Если изображение является презентационным,altможет быть пустой строкой или<img>обязан иметьrole="presentation". eslint:jsx-a11y/img-has-alt// плохо <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.
```jsx
// плохо
<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. (почему?)
// плохо
{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 }) {
return <div>{foo}{bar}</div>;
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
SFC.defaultProps = {
bar: '',
children: null,
};-
Всегда используйте функции обратного вызова. eslint:
react/no-string-refs// плохо <Foo ref="myRef" /> // хорошо <Foo ref={(ref) => { this.myRef = ref; }} />
-
Оборачивайте в скобки JSX теги, когда они занимают больше одной строки. eslint:
react/jsx-wrap-multilines// плохо render() { return <MyComponent className="long body" foo="bar"> <MyChild /> </MyComponent>; } // хорошо render() { return ( <MyComponent className="long body" foo="bar"> <MyChild /> </MyComponent> ); } // хорошо, когда одна строка render() { const body = <div>hello</div>; return <MyComponent>{body}</MyComponent>; }
-
Всегда используйте самозакрывающиеся теги, если у элемента нет дочерних элементов. eslint:
react/self-closing-comp// плохо <Foo className="stuff"></Foo> // хорошо <Foo className="stuff" />
-
Если ваш компонент имеет множество свойств, которые располагаются на нескольких строчках, то закрывайте тег на новой строке. eslint:
react/jsx-closing-bracket-location// плохо <Foo bar="bar" baz="baz" /> // хорошо <Foo bar="bar" baz="baz" />
-
Используйте стрелочные функции для замыкания локальных переменных.
function ItemList(props) { return ( <ul> {props.items.map((item, index) => ( <Item key={item.key} onClick={() => doSomethingWith(item.name, index)} /> ))} </ul> ); }
-
Привязывайте обработчики событий для метода
renderв конструкторе. eslint:react/jsx-no-bind
Почему? Вызов
bindв методеrenderсоздает новую функцию при каждой перерисовке.
```jsx
// плохо
class extends React.Component {
onClickDiv() {
// ...
}
render() {
return <div onClick={this.onClickDiv.bind(this)} />
}
}
// хорошо
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 для более глубокого обсуждения.
```jsx
// плохо
React.createClass({
_onClickSubmit() {
// ...
},
// ...
});
// хорошо
class extends React.Component {
onClickSubmit() {
// ...
}
// ...
}
```
-
Всегда возвращайте значение в методах
render. eslint:react/require-render-return// плохо render() { (<div />); } // хорошо render() { return (<div />); }
- Последовательность для
class extends React.Component:
- произвольные
staticметоды constructorgetChildContextcomponentWillMountcomponentDidMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdatecomponentWillUnmount- обработчики кликов или событий, такие как
onClickSubmit()илиonChangeDescription() - getter методы для
render, такие какgetSelectReason()илиgetFooterContent() - произвольные render методы, такие как
renderNavigation()илиrenderProfilePicture() render
-
Как определять
propTypes,defaultProps,contextTypes, и т.д.import React, { PropTypes } from 'react'; 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
displayNamepropTypescontextTypeschildContextTypesmixinsstaticsdefaultPropsgetDefaultPropsgetInitialStategetChildContextcomponentWillMountcomponentDidMountcomponentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdatecomponentWillUnmount- обработчики кликов или событий, такие как
onClickSubmit()илиonChangeDescription() - getter методы для
render, такие какgetSelectReason()илиgetFooterContent() - произвольные render методы, такие как
renderNavigation()илиrenderProfilePicture() render
- Не используйте
isMounted. eslint:react/no-is-mounted
Почему?
isMounted— это антипаттерн, который недоступен при использовании ES6 классов и который планируют официально признать устаревшим.
Это JSX/React руководство также доступно и на других языках:
Китайский (Упрощенный): JasonBoy/javascript
Польский: pietraszekl/javascript
Корейский: apple77y/javascript
Португальский: ronal2do/javascript
Японский: mitsuruog/javascript-style-guide
Испанский: agrcrobles/javascript
Украинский: ivanzusko/javascript
Русский: leonidlebedev/javascript-airbnb