592
523

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 5 years have passed since last update.

2017末時点での React Component 設計のベストプラクティス

Last updated at Posted at 2017-12-17

どう考えているか、というのを聞かれたので、記事に起こしておきます。個人の意見です。

Prettier を使う

気づけばコードの整形を人間がやる時代は終わりました。
細かいコーディングスタイルでレビューの時間を取るぐらいだったら、一貫した自動整形ルールを適用すべきです。

人によっては細かいこだわりがあって prettier の規則が気に食わないかもしれず、僕も最初はそうでしたが、Atomで保存する度に自動整形を走らせる prettier の強烈な開発体験によって、最終的にそれらのこだわりを全て捨てることが出来ました。

生産性を求めるなら、現時点では最優先で導入すべきものです。

React.createClass を使わない

v16 で削除されたのでいわずもがな。
同様に、 createClass でしか使えなかった mixin 周辺機能も丸ごと deprecated です。

「可能な限りは」 Stateless Fuctional Component を使う

悪い例

これはアンチパターンとは言い切れないけど、今ではあまりいいコンポーネント定義ではない例です。

class App extends React.Component {
  componentDidMount() {
    console.log('mounted')
  }
  render() {
    return <span>Hello</span>
  }
}

理由

クラスベースの設計は状態を内部で抱えがちで、React.Component の内部においては state は好まれません。プレゼンテーショナルなレイヤーだということを明示するために、state に触らない限りは extends React.Component は推奨しません。

現実的には、state は完全に排除出来ないとは思いますが、 state を持つ component は特殊なコンテナーであって、出来る限り抑えるべきです。

それを機械的に検出するのに eslint-plugin-react/prefer-stateless-function をおすすめしています。

代替

HOC + Functional Stateless Component

とりあえずは recompose を使うのを推奨しています。

import { lifecycle } from 'recompose'

lifecycle({
  componentDidMount() {
    console.log('hello')
  }
})(function App () {
  return <span>Hello</span>
})

複数の HOC を使う場合は compose を使います。

import { compose, lifecycle, pure } from 'recompose'

compose(
  lifecycle({
    componentDidMount() {
      console.log('hello')
    }
  }),
  pure
})(function App () {
  return <span>Hello</span>
})

redux を使っているならば、 コールバックから dispatch することはあっても、状態そのものは component 内部に残らないはず。
結果として、 extends する React Component はほとんどいなくなるはずです。

内部関数で子Component を render しない

悪い例

よく見る駄目な例。

class Layout extends React.Component {
  renderHeader() {
    return <header/>
  }
  render() {
    return <div>
      {this.renderHeader()}
      {this.props.children}
      </div>
  }
}

良い例

責務に則って分割します。

function Header() {
  return <header/>
}

function Layout({children}) {
  return <div>
    <Header/>
    {children}
  </div>
}

本当に必要になるまで ImmutableJS を使わない

理由

これは議論の余地があるとは思っているのですが、自分の経験上、 Immutable JS は、一部のドメインを無駄にややこしくするだけで、理解せずに使うとパフォーマンス問題を解決するツールなのに重篤なパフォーマンス問題を引き起こしがちで、そしてほとんどの人はこれを使う意味を理解していません。とくにチーム開発においては、百害あって一理なしだと思っています。

代替

ES2015 の object-rest-spread を使う

const prev = { a: 1, b: 1 }
const next = { ...prev, b: 2}

Reactの差分検知で、オブジェクト参照が同じままその内部状態を抱えてしまうと、変更検知されない可能性があります。

もっと攻めたルールだと、React に限らずオブジェクトのメンバへの再代入禁止、もありえます。あらゆるオブジェクトは暗黙にイミュータブルとして扱うというわけです。自分の個人プロジェクトはそれで運用して、ちゃんと回っています。

PropTypes はもう使わない

理由

shape や required といった特殊な記法を覚えさせられるのに、ランタイムチェックしか出来ない、というのは静的解析ツールが充実した今では、二重定義になり足枷になりつつあります。

おそらくですが、Facebookの中の人達はもう使ってないです。古い時代の名残で残ってる機能のように見えます。この機能に限らず、Reactの機能が拡充される方向性は、Facebook社自身の内部での使い方に強く依存します。

代替案

Flow / TypeScript を使ってください。どちらも JSX の props の型情報を静的に検証できます。

Action定義では Flux Standard Action に従う

React からやや外れますが、Action 定義で FSA に準拠していない人が多いです。

一部の redux middleware は正しくFSAに準拠しないとちゃんと動かなかったりします。

悪い例

{
  type: 'move-to',
  x: 1,
  y: 3
}

良い例

{
  type: 'move-to',
  payload: {
    x: 1,
    y: 3
  }
}

まとめ

  • Prettier を使う
  • Stateless Component を優先する
  • プレゼンテーショナルでない振る舞いの定義は HOC で行う
  • PropTypes より flow/typescript を使う
  • FSAに従う
  • JSのオブジェクトをイミュータブルだと思い込んで使う
592
523
2

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
592
523

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?