JavaScript
JSX
reactjs
React

[翻訳] Airbnb React/JSX Style Guide


この翻訳について

Airbnb React/JSX Style Guideの和訳です。

間違っていたり分かりにくい箇所があれば、ご指摘いただけると幸いです。


Airbnb React/JSX スタイルガイド

このスタイルガイドは現在一般的に使用されている標準に基いていますが、場合によってはいくつかの慣例(async/awaitやstatic class fields)が含まれていたり禁止されていたりします。現在、このガイドにはステージ3より前のものは含まれておらず非推奨です。


目次


  1. 基本的なルール

  2. クラス vs React.createClass vs ステートレス

  3. ミックスイン

  4. 命名規則

  5. 宣言

  6. アラインメント

  7. 引用符

  8. 空白

  9. 引数

  10. 参照

  11. 括弧

  12. タグ

  13. メソッド

  14. 順序

  15. isMounted


基本的なルール


  • Reactコンポーネントは1ファイルに1つだけにしてください。



  • 常にJSXの構文を使用してください。

  • JSXでないファイルでアプリを初期化している場合を除き、React.createElementは使用しないでください。


クラス vs React.createClass vs ステートレス

// 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を渡すとき、displayNamewithFoo(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コンポーネントの引数名を使用しないでください。


一般的にstyleclassNameのような引数は、特定のものを意味すると考えます。アプリのサブセットに対してこの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 {
}


アラインメント

// 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 />}


引用符


  • 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' }} />


空白

// bad

<Foo/>

// very bad
<Foo />

// bad
<Foo
/>

// good
<Foo />

// bad

<Foo bar={ baz } />

// good
<Foo bar={baz} />


引数


  • 引数名はキャメルケースを使ってください。

// bad

<Foo
UserName="hello"
phone_number={12345678}
/>

// good
<Foo
userName="hello"
phoneNumber={12345678}
/>

// 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" />

// bad - ARIA roleでない

<div role="datepicker" />

// bad - 抽象的なARIA role
<div role="range" />

// good
<div role="button" />


スクリーンリーダーやキーボードを使う人々が使用するキーボードショートカットとキーボードコマンド間の不一致は、アクセシビリティを複雑にしてしまいます。


// 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} />
}


参照

// bad

<Foo
ref="myRef"
/>

// good
<Foo
ref={(ref) => { this.myRef = ref; }}
/>


括弧

// 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>;
}


タグ

// bad

<Foo variant="stuff"></Foo>

// good
<Foo variant="stuff" />

// bad

<Foo
bar="bar"
baz="baz" />

// good
<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メソッドに対してイベントハンドラをバインドしてください。


render内でのバインドは、レンダリングのたびに新しい関数を作成してしまいます。


// bad

class extends React.Component {
onClickDiv() {
// do stuff
}

render() {
return <div onClick={this.onClickDiv.bind(this)} />;
}
}

// 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
}

// bad

render() {
(<div />);
}

// good
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. onClickSubmit()onChangeDescription()のようなクリックハンドラまたはイベントハンドラ


  12. getSelectReason()getFooterContent()のようなrender用のゲッターメソッド


  13. renderNavigation()renderProfilePicture()のような任意のレンダリングメソッド

  14. render



  • propTypesdefaultPropscontextTypesなどの定義方法

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;


  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. getSelectReason()getFooterContent()のようなrender用のゲッターメソッド


  20. renderNavigation()renderProfilePicture()のような任意のレンダリングメソッド


  21. renderr`


isMounted


isMountedはアンチパターンであり、ES6では使用できず、今後正式に廃止される予定です。