676
599

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[翻訳] Airbnb React/JSX Style Guide

Last updated at Posted at 2016-05-29

この翻訳について

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は使用しないでください。
  • react/forbid-prop-typesは、arrayOfobjectOf、またはshapeを使用してarrayおよびobjectに含まれるかが明示的に示されている場合にのみ、arraysobjectsを許可します。

クラス 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 />}

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

空白

// bad
<Foo/>

// very bad
<Foo                 />

// bad
<Foo
 />

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

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

引数

  • 引数名はキャメルケースを使用し、引数がReactコンポーネントの場合はパスカルケースを使用してください。
// bad
<Foo
  UserName="hello"
  phone_number={12345678}
/>

// good
<Foo
  userName="hello"
  phoneNumber={12345678}
  Component={SomeComponent}
/>
// 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"
/>

メソッド

  • ローカル変数を閉じるようにアロー関数を使用してください。追加のデータをイベントハンドラーに渡す必要がある場合に便利です。ただし、特に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
}
// 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. handleSubmit()handleChangeDescription() のように handle で始まるイベントハンドラ
    13. onClickSubmit()onChangeDescription() のように on で始まるイベントハンドラ
    14. getSelectReason()getFooterContent()のような*render用のゲッターメソッド*
    15. renderNavigation()renderProfilePicture()のような任意のレンダリングメソッド
    16. 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. render

isMounted

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

676
599
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
676
599

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?