Edited at

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

More than 1 year has passed since last update.

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


Prettier を使う

https://github.com/prettier/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 をおすすめしています。

https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md


代替

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 に準拠していない人が多いです。

https://github.com/acdlite/flux-standard-action

一部の 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のオブジェクトをイミュータブルだと思い込んで使う