この翻訳について
Airbnb React/JSX Style Guideの和訳です。
間違っていたり分かりにくい箇所があれば、ご指摘いただけると幸いです。
Airbnb React/JSX スタイルガイド
このスタイルガイドは現在一般的に使用されている標準に基いていますが、場合によってはいくつかの慣例(async/awaitやstatic class fields)が含まれていたり禁止されていたりします。現在、このガイドにはステージ3より前のものは含まれておらず非推奨です。
目次
- 基本的なルール
- クラス vs
React.createClass
vs ステートレス - ミックスイン
- 命名規則
- 宣言
- アラインメント
- 引用符
- 空白
- 引数
- 参照
- 括弧
- タグ
- メソッド
- 順序
isMounted
基本的なルール
- Reactコンポーネントは1ファイルに1つだけにしてください。
- ただし、1ファイルに複数のステートレスなコンポーネントを含むのは良いです。(eslint:
react/no-multi-comp
)
- ただし、1ファイルに複数のステートレスなコンポーネントを含むのは良いです。(eslint:
- 常にJSXの構文を使用してください。
- JSXでないファイルでアプリを初期化している場合を除き、
React.createElement
は使用しないでください。 -
react/forbid-prop-types
は、arrayOf
、objectOf
、またはshape
を使用してarray
およびobject
に含まれるかが明示的に示されている場合にのみ、arrays
とobjects
を許可します。
クラス vs React.createClass
vs ステートレス
React.createClass
vs ステートレス- stateやrefsを使う場合、
React.createClass
よりもclass extends React.Component
が好まれます。(eslint:react/prefer-es6-class
react/prefer-stateless-function
)
// bad
const Listing = React.createClass({
// ...
render() {
return <div>{this.state.hello}</div>;
}
});
// good
class Listing extends React.Component {
// ...
render() {
return <div>{this.state.hello}</div>;
}
}
また、stateやrefsを使わない場合、クラスよりも通常の関数(アロー関数ではない) が好まれます。
// bad
class Listing extends React.Component {
render() {
return <div>{this.props.hello}</div>;
}
}
// bad - 関数名の推測が必要となる
const Listing = ({ hello }) => (
<div>{hello}</div>
);
// good
function Listing({ hello }) {
return <div>{hello}</div>;
}
ミックスイン
ミックスインは暗黙の依存関係により、名前の衝突を引き起こし、雪だるま式に複雑になります。ミックスインの使用事例のほとんどは、コンポーネント、高次コンポーネント、またはユーティリティモジュールを介して、より良い方法で実装できます。
命名規則
-
拡張子:
.jsx
を使用してください。(eslint:react/jsx-filename-extension
) -
ファイル名: パスカルケースを使用してください。(例:
ReservationCard.jsx
) -
参照名: Reactコンポーネントにはパスカルケースを使用し、それらのインスタンスにはキャメルケースを使用してください。(eslint:
react/jsx-pascal-case
)
// bad
import reservationCard from './ReservationCard';
// good
import ReservationCard from './ReservationCard';
// bad
const ReservationItem = <ReservationCard />;
// good
const reservationItem = <ReservationCard />;
-
コンポーネントの命名規則: コンポーネント名としてファイル名を使ってください。例えば、
ReservationCard.jsx
は、ReservationCard
という参照名を持つ必要があります。ただし、ディレクトリのルートコンポーネントの場合は、ファイル名としてindex.jsx
を使用し、コンポーネント名としてディレクトリ名を使ってください。
// bad
import Footer from './Footer/Footer';
// bad
import Footer from './Footer/index';
// good
import Footer from './Footer';
-
高次コンポーネントの命名規則: 高次コンポーネントの名前と渡されたコンポーネントの名前を合わせたものを、生成されたコンポーネントの
displayName
として使用します。例えば、高次コンポーネントwithFoo()
がコンポーネントBar
を渡すとき、displayName
がwithFoo(Bar)
のコンポーネントを生成すべきです。
コンポーネントの
displayName
は開発者ツールやエラーメッセージで使用され、この関係をはっきりと示す値を持つことで、何が起こっているのかを理解するのに役立ちます。
// bad
export default function withFoo(WrappedComponent) {
return function WithFoo(props) {
return <WrappedComponent {...props} foo />;
}
}
// good
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を変更すると、コードの読みやすさと保守性が低下し、バグが発生する可能性があります。
// bad
<MyComponent style="fancy" />
// bad
<MyComponent className="fancy" />
// good
<MyComponent variant="fancy" />
宣言
- コンポーネントの命名に
displayName
は使わないでください。代わりに、参照によってコンポーネントに命名してください。
// bad
export default React.createClass({
displayName: 'ReservationCard',
// stuff goes here
});
// good
export default class ReservationCard extends React.Component {
}
アラインメント
- 以下の形式に従ってください。(eslint:
react/jsx-closing-bracket-location
)
// bad
<Foo superLongParam="bar"
anotherSuperLongParam="baz" />
// good
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
// 一行に収まるならば、その行にまとめてください
<Foo bar="bar" />
// 子要素はインデントを付けます
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
>
<Quux />
</Foo>
// bad
{showButton &&
<Button />
}
// bad
{
showButton &&
<Button />
}
// good
{showButton && (
<Button />
)}
// good
{showButton && <Button />}
// good
{someReallyLongConditional
&& anotherLongConditional
&& (
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
)
}
// good
{someConditional ? (
<Foo />
) : (
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>
)}
引用符
- JSXの引数にはダブルクオート(
"
)を使用し、その他のJSにはシングルクオート('
)を使用してください。(eslint:jsx-quotes
)
通常のHTMLの属性は一般的にシングルクオートの代わりにダブルクオートを使用するため、JSXの属性もこの慣習に倣っています。
// bad
<Foo bar='bar' />
// good
<Foo bar="bar" />
// bad
<Foo style={{ left: "20px" }} />
// good
<Foo style={{ left: '20px' }} />
空白
- 空要素のタグの中にはスペースを1つ入れてください。(eslint:
no-multi-spaces
,react/jsx-tag-spacing
)
// bad
<Foo/>
// very bad
<Foo />
// bad
<Foo
/>
// good
<Foo />
- JSXの中括弧の中にはスペースは入れないでください。(eslint:
react/jsx-curly-spacing
)
// bad
<Foo bar={ baz } />
// good
<Foo bar={baz} />
引数
- 引数名はキャメルケースを使用し、引数がReactコンポーネントの場合はパスカルケースを使用してください。
// bad
<Foo
UserName="hello"
phone_number={12345678}
/>
// good
<Foo
userName="hello"
phoneNumber={12345678}
Component={SomeComponent}
/>
- 引数の値が明らかに
true
である場合には省略してください。(eslint:react/jsx-boolean-value
)
// bad
<Foo
hidden={true}
/>
// good
<Foo
hidden
/>
// good
<Foo hidden />
-
<img>
タグにはalt
引数を含めてください。もし、画像が表現的なコンテンツである場合、alt
を空の文字列するか、role="presentation"
を含めてください。(eslint:jsx-a11y/alt-text
)
// bad
<img src="hello.jpg" />
// good
<img src="hello.jpg" alt="Me waving hello" />
// good
<img src="hello.jpg" alt="" />
// good
<img src="hello.jpg" role="presentation" />
-
<img>
のalt
引数には、"image"、"photo"、"picture"のような言葉を使用しないでください。(eslint:jsx-a11y/img-redundant-alt
)
スクリーンリーダーは
img
要素を既に画像として知らせているので、altテキストにこの情報を含める必要がありません。
// bad
<img src="hello.jpg" alt="Picture of me waving hello" />
// good
<img src="hello.jpg" alt="Me waving hello" />
- 有効で非抽象的なARIA rolesのみ使用してください。(eslint:
jsx-a11y/aria-role
)
// bad - ARIA roleでない
<div role="datepicker" />
// bad - 抽象的なARIA role
<div role="range" />
// good
<div role="button" />
- 要素に
accessKey
を使わないでください。(eslint:jsx-a11y/no-access-key
)
スクリーンリーダーやキーボードを使う人々が使用するキーボードショートカットとキーボードコマンド間の不一致は、アクセシビリティを複雑にしてしまいます。
// bad
<div accessKey="h" />
// good
<div />
- 配列のインデックスを
key
引数に使うのは避けてください。一意のIDが好まれます。(eslint:react/no-array-index-key
)
安定したIDを使用しないと、パフォーマンスに悪影響を及ぼし、コンポーネントのstateに問題が生じる可能性があるためアンチパターンです。
要素の順序が変わる可能性がある場合は、keyにindexを使用することはおすすめできません。
// bad
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}
// good
{todos.map(todo => (
<Todo
{...todo}
key={todo.id}
/>
))}
- 必須ではないすべての引数に対して常に明示的な
defaultProps
を定義してください。
propTypes
はドキュメントの一種であり、defaultProps
を提供するということは、あなたのコードの読者がそれほど多くを引き受ける必要がないことを意味します。また、コードで特定の型チェックを省略することもできます。
// bad
function SFC({ foo, bar, children }) {
return <div>{foo}{bar}{children}</div>;
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
// good
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 v15.6.1以下では、無効なHTML属性を渡すことができます。
例外:
- 引数を渡しpropTypesを巻き上げるプロキシとしてのHOC。
function HOC(WrappedComponent) {
return class Proxy extends React.Component {
Proxy.propTypes = {
text: PropTypes.string,
isLoading: PropTypes.bool
};
render() {
return <WrappedComponent {...this.props} />;
}
}
}
- 明確な引数とオブジェクトのスプレッド構文。これはMochaのbeforeEachを使ってReactコンポーネントをテストする際に便利です。
export default function Foo {
const props = {
text: '',
isPublished: false
}
return (<div {...props} />);
}
使用上の注意:
可能であれば不要な引数は取り除いてください。またバグを防ぐためprop-types-exactを使用してください。
// bad
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...this.props} />
}
// good
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...relevantProps} />
}
参照
- 常に参照コールバックを使用してください。(eslint:
react/no-string-refs
)
// bad
<Foo
ref="myRef"
/>
// good
<Foo
ref={(ref) => { this.myRef = ref; }}
/>
括弧
- JSXのタグが複数行になる場合は括弧で囲ってください。(eslint:
react/jsx-wrap-multilines
)
// bad
render() {
return <MyComponent variant="long body" foo="bar">
<MyChild />
</MyComponent>;
}
// good
render() {
return (
<MyComponent variant="long body" foo="bar">
<MyChild />
</MyComponent>
);
}
// good - 一行なので
render() {
const body = <div>hello</div>;
return <MyComponent>{body}</MyComponent>;
}
タグ
- 子要素がない場合は以下のように記述してください。(eslint:
react/self-closing-comp
)
// bad
<Foo variant="stuff"></Foo>
// good
<Foo variant="stuff" />
- 複数行の引数を持つコンポーネントの場合、新しい行でタグを閉じてください。(eslint:
react/jsx-closing-bracket-location
,react/jsx-closing-tag-location
)
// bad
<Foo
bar="bar"
baz="baz" />
// good
<Foo
bar="bar"
baz="baz"
/>
メソッド
- ローカル変数を閉じるようにアロー関数を使用してください。追加のデータをイベントハンドラーに渡す必要がある場合に便利です。ただし、特にPureComponentsである可能性のあるカスタムコンポーネントに渡される場合は、パフォーマンスが大幅に低下しないようにしてください。なぜなら、毎回不必要な再レンダリングをトリガーするためです。
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
)
render内でのバインドは、レンダリングのたびに新しい関数を作成してしまいます。クラスのフィールドでアロー関数を使用しないでください。テストとデバッグが難しくなり、パフォーマンスに悪影響を及ぼす可能性があります。概念的には、クラスのフィールドはロジック用ではなくデータ用です。
// bad
class extends React.Component {
onClickDiv() {
// do stuff
}
render() {
return <div onClick={this.onClickDiv.bind(this)} />;
}
}
// very bad
class extends React.Component {
onClickDiv = () => {
// do stuff
}
render() {
return <div onClick={this.onClickDiv} />
}
}
// good
class extends React.Component {
constructor(props) {
super(props);
this.onClickDiv = this.onClickDiv.bind(this);
}
onClickDiv() {
// do stuff
}
render() {
return <div onClick={this.onClickDiv} />;
}
}
- Reactコンポーネントのメソッドにアンダースコアの接頭辞を使わないでください。
アンダースコアのプレフィックスは、privateを示すために他の言語の規約として使用されることがあります。しかし、これらの言語とは異なり、JavaScriptではprivateのネイティブサポートはなく、すべてがpublicです。あなたの意図にかかわらず、あなたのプロパティにアンダースコアのプレフィックスを追加しても、それを実際にprivateにするわけではなく、任意のプロパティ(アンダースコアのプレフィックス付きかどうか)をpublicとして扱うべきです。詳細は、#1024、#490を参照してください。
// bad
React.createClass({
_onClickSubmit() {
// do stuff
},
// other stuff
});
// good
class extends React.Component {
onClickSubmit() {
// do stuff
}
// other stuff
}
-
render
メソッドでは値を返すようにしてください。(eslint:react/require-render-return
)
// bad
render() {
(<div />);
}
// good
render() {
return (<div />);
}
順序
-
class extends React.Component
用の順序- 任意の
static
メソッド constructor
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
-
onClickSubmit()
、onChangeDescription()
のようなクリックハンドラまたはイベントハンドラ -
handleSubmit()
、handleChangeDescription()
のように handle で始まるイベントハンドラ -
onClickSubmit()
、onChangeDescription()
のように on で始まるイベントハンドラ -
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()
のようなクリックハンドラまたはイベントハンドラ -
getSelectReason()
、getFooterContent()
のような*render
用のゲッターメソッド* -
renderNavigation()
、renderProfilePicture()
のような任意のレンダリングメソッド render
isMounted
isMounted
-
isMounted
は使わないでください。(eslint:react/no-is-mounted
)
isMounted
はアンチパターンであり、ES6では使用できず、今後正式に廃止される予定です。