React hooksが何故うれしいのか、Reactの今までを含めて解説

# はじめに
[アルプ株式会社](https://thealp.co.jp/)でフロントエンドをしているmura-と申します。
弊社サービスのSaaSを管理するtoBむけプラットフォーム [Scalebase](https://www.scalebase.com/)ではReactを使ってフロントを構築しており、最近話題になっているhooksも導入しています。
昨年から発表はありましたが、今年の2月に、正式にhooksが利用できるようになったReact v16.8.0がリリースされました。

hooksはReact界にとってかなり良いインパクトを残し、hooksとはなにか、どう便利なのかという記事や話題をたくさん目にするようになったと思います。 
hooksを導入することのメリット自体は調べればたくさんでてきますが、この記事ではReactの今まで提供されたコンポーネント変遷を交えて、最終的になぜhooksが導入されもてはやされるようになったかという視点で説明していきます。  

# TL;DR
忙しい方は [今までの問題点とhooksが解決すること](https://qiita.com/_muraham/items/d31628e15ed6ff6e3f58#%E4%BB%8A%E3%81%BE%E3%81%A7%E3%81%AE%E5%95%8F%E9%A1%8C%E7%82%B9%E3%81%A8hooks%E3%81%8C%E8%A7%A3%E6%B1%BA%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8) をご覧ください


# Reactの変遷

## 基本的なComponentの定義
hooksを駆使したものを除き、Componentの定義には `createClass()` と `React.Component` を継承したClassでの定義があります。

### React.createClassを使った、React初期のComponentの定義
Class構文に対応している他の言語であれば、Component指向にしようとするとClassで表現するのを思いつきますが、当時Class構文に対応したES2015がまだ正式にリリースされてなかったのもあり、`React.createClass()` でClass構文を使わずComponentを作成するメソッドが用意されていました。

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="mura-the-looper" data-slug-hash="YzPqeNd" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Use React Create Class">
  <span>See the Pen <a href="https://codepen.io/mura-the-looper/pen/YzPqeNd">
  Use React Create Class</a> by Kazuki Murahama (<a href="https://codepen.io/mura-the-looper">@mura-the-looper</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

createClassによる定義でも今と同じようにライフサイクルメソッドやStateの初期値、渡すPropsなどを引数のオブジェクトに定義する必要がありました。  
当時はVisual Studio CodeがなくTypeScriptもサポートされてなかったですし、普通のエディタでカジュアルに触ると、長大なオブジェクトの定義でカンマのつけ忘れなどでsyntax errorを起こしてしまうも多くあったことを覚えています。

### React.Componentを継承したClassBasedなコンポーネントの定義

Reactは、[v0.13から正式にES2015のClass構文に対応](https://reactjs.org/blog/2015/01/27/react-v0.13.0-beta-1.html)しました。  

React.ComponentをextendsしてClassを定義すると、そのClassのインスタンスメソッドとしてライフサイクルメソッドを定義できます。createClassと同じく各インスタンスは `render()` が必ず実行されます。

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="mura-the-looper" data-slug-hash="OJPXWXX" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Use React Class based component">
  <span>See the Pen <a href="https://codepen.io/mura-the-looper/pen/OJPXWXX">
  Use React Class based component</a> by Kazuki Murahama (<a href="https://codepen.io/mura-the-looper">@mura-the-looper</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

Classですので、constructorになにか処理をさせたり、Stateをインスタンス変数として定義できます。  
定義において基本的には、React.createClassでやっていたことがClassで書けるようになったことにより、よりComponentらしく書けるようになりました。

※ライフサイクルメソッドはv16.3から大きく変更しています。
参考: [React v16.3 changes](https://blog.koba04.com/post/2018/04/04/react-v163-changes/)

## パフォーマンスを考慮したコンポーネントの定義

`React.Component` だけだとシンプルに書きたい場合やパフォーマンスを考慮したときに難点がでてきました。それを解消するためのStateless Functional Componentと `React.PureComponent` が用意されました。

### 関数で定義するStateless functional componentの定義の仕方

Classを使って書けるのは開発者としては嬉しいですが、Stateすら持たず、ただ `render()` だけしたいだけのComponentなど、通常のClassによる定義がオーバースペックになる場合がありました。  
そこでシンプルでカジュアルに定義できるStateless functional component (SFC)がv0.14で用意されました。  

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="mura-the-looper" data-slug-hash="dyPXNzb" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="StatelessFunctinalComponent">
  <span>See the Pen <a href="https://codepen.io/mura-the-looper/pen/dyPXNzb">
  StatelessFunctinalComponent</a> by Kazuki Murahama (<a href="https://codepen.io/mura-the-looper">@mura-the-looper</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

JSXをreturnする関数を書けばいいだけなのでだいぶ見通しもいいです。[メモリを確保する必要がないことと、React自体がSFCのパフォーマンスを最適化させているので](https://reactjs.org/blog/2015/10/07/react-v0.14.html#stateless-functional-components)パフォーマンス的にも向上がはかれます。( [45%速くなった事例](https://medium.com/missive-app/45-faster-react-functional-components-now-3509a668e69f)もあるようです。)  
  
ただし、個人的には大きなデメリットがあり、SFCを定義したあとでStateをもたせたくなった場合、通常のClassに書き換えざるを得ず大変でした。だったら、最初から通常のComponentを定義しよう、と思っている人もいたのではないかと思います。  


### React.PureComponentを使ったコンポーネントの定義

Reactでは親のコンポーネントのStateが変わったときなど、その下に含まれる子コンポーネントまで再描画されます。仮に子コンポーネントに渡しているPropsに差分がなくても、再描画してしまうので、子コンポーネントで難しい計算などをしていた場合パフォーマンスに影響がでたりします。[shouldComponentUpdate()](https://ja.reactjs.org/docs/react-component.html#shouldcomponentupdate)  を使うことでコンポーネントを再描画しないようフラグをたてることができますが、毎回それを書くのは手間ですし忘れます。  
v15.3でリリースされた `React.PureComponent` は、基本的には `React.Component` と変わらないのですが、自動的に `shouldComponentUpdate()` を実行して必要以上に再描画しないよう、取り計らってくれます。（ただし、Shallowな比較なのでObjectやArrayのPropsを渡している場合は注意が必要です）  

## hooksが登場するまでに使われていたComponentと用途のまとめ

諸々経緯があり、いろんなComponentが用意されていますが、hooksが登場するv16.8以前は、通常は下記のような用途で使われていました  

| Componentの種類 | 用途 | 備考 |
| --- | --- | --- |
| React.createClass()による定義 | ES2015などモダンな環境でない場合に使われる。そうでなければ通常使わない。 | 最近はReactDOMのようにReact本体からも切り離されている |
| React.Componentを継承したClass | Stateをもったり、親Componentとして使われる。通常使われるComponent。 |  |
| Stateless Functional Component (関数Component) | Stateを持たない、難しいことをさせたりしないテンプレートのような用途で使われる。|  |
| React.PureComponentを継承したClass | Stateをもち、子コンポーネントなどで複雑な計算などをする場合使われる。 | v16.6で追加された `React.memo()` を使えば, Stateさえ持たなければSFCでも役割を担うことができるようになった。もちろん通常のComponentで `shouldComponentUpdate()` を定義すれば同等なことができるのでプロジェクト次第では使われてないかもしれません。|

## Componentに共通する振る舞いを与える
ここまでComponent自体について紹介しましたが、定義したComponentに対して、ロジックが一元化された任意の振る舞いやデータを与えたいケースもあります。Componentに任意の振る舞いを与えるために、どうしてきたかということも紹介します。  

### React.createClass() にのみ存在したMixin
Mixinはかなり便利な機能でした。reactのライフサイクルメソッドとして、`componentDidMount` や `componentDidUpdate` などがありますが、任意のComponentに好きに振る舞いを注入させることができました。  
  
[React.jsのmixinについて](https://qiita.com/koba04/items/55d28f0aba4d1f53e735)

### Higher-order Componentを使って振る舞いを与える

ClassBasedなReactではMixinがサポートされなくなりました。[Mixinは問題があり](https://ja.reactjs.org/blog/2016/07/13/mixins-considered-harmful.html) ますし、公式でもHigher−order Componentを使えとアナウンスされてました。  
  
Higher-order Component(HoC)自体はReact自体の機能ではなく、あるComponentにPropsや機能などを渡して返すためのイディオムです。Redux Reactの [connect()](https://react-redux.js.org/api/connect) など、ライブラリが作るデータや機能を、Componentに渡すときなどによく使われる手法です。  

```js
// react reduxのconnectを使ってReduxのStateやPropsを渡す
connect()(MyComponent)
connect(mapState)(MyComponent)
connect(
  mapState,
  null,
  mergeProps,
  options
)(MyComponent)
```

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="mura-the-looper" data-slug-hash="abzZQrg" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="abzZQrg">
  <span>See the Pen <a href="https://codepen.io/mura-the-looper/pen/abzZQrg">
  abzZQrg</a> by Kazuki Murahama (<a href="https://codepen.io/mura-the-looper">@mura-the-looper</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

HoCに関する詳細は下記をごらんください。  
[高階（Higher-Order）コンポーネント](https://ja.reactjs.org/docs/higher-order-components.html)

### render propを使って振る舞いを与える

Reactが提供する [render prop](https://ja.reactjs.org/docs/render-props.html) というものを使って振る舞いを与えることもできます。
render propとHoC、好きな方法を使うことができますが、HoCは名前衝突したり記述が煩雑になったりしますし、型の定義が大変ですのでrender propの方が書きやすいとは思います。

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="mura-the-looper" data-slug-hash="xxbOgXe" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="RenderPropsComponent">
  <span>See the Pen <a href="https://codepen.io/mura-the-looper/pen/xxbOgXe">
  RenderPropsComponent</a> by Kazuki Murahama (<a href="https://codepen.io/mura-the-looper">@mura-the-looper</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# hooksを使ったComponentの定義と、今までの問題点とhooksが解決すること
ここまで、長くなってしまいましたが今まで紹介したことを踏まえて、hooksを比較してみます。

## hooksで定義したComponent

hooksを使うと、下記のようにComponentが定義できます。

<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="mura-the-looper" data-slug-hash="NWPrdjj" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="React Functional Component">
  <span>See the Pen <a href="https://codepen.io/mura-the-looper/pen/NWPrdjj">
  React Functional Component</a> by Kazuki Murahama (<a href="https://codepen.io/mura-the-looper">@mura-the-looper</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>


まず、大きな点として、React.Componentを使ってClassとして定義しなくて良くなった点が大きいです。  
Stateless functional component（SFC）について先程紹介しましたが、Functional Componentなのに状態を持つことができるようになった、と言うことができます。ここでは仕組みについて言及はしませんが、reactは `useState` などのAPIを提供したおかげで、関数コンポーネントでStateを扱うことができるようになりました。  
また、今まで `componentDidMount` などで扱っていたであろう、コンポーネントに対する副作用も `useEffect` などを使って表現できます。  
ただそれだけ、といえばそれだけなのですが、今までのReactの問題点を考えるとかなりインパクトがあることがわかります。  

## 今までの問題点とhooksが解決すること

- 今までのReactの問題点
    - どのコンポーネントを使っていくか都度考えないといけない。
        - SFCを定義したとしてもあとからStateをもたせたくなってClassに書き換える手間がある。
        - PureComponentを使ったとしても結局最適化できない場合があり `shouldComponentUpdate()` を定義する手間が発生する。など
    - ライフサイクルメソッドの複雑・煩雑さ
        - 一番最初に動作するライフサイクルメソッドはどれか、どのライフサイクルメソッドで副作用がおきるか、関連するロジックであってもライフサイクルごとに定義しないといけないので、いろんなロジックが混ざるとかなり複雑になりバグを生む。
    - Wrapper Hell
        - HoC, render propなどを駆使したとて結局、Componentのネストが深くなってしまい可読性がおちる

※上記は主観もふくまれますので [hooksが実装された動機](https://ja.reactjs.org/docs/hooks-intro.html#motivation)もごらんください  

- hooksが解決すること
    - Componentは基本的にすべてFunctional Componentを使って定義してよい
    - ライフサイクルメソッドから開放され、ロジックはreturnするJSXの前にすべて表現できる
      そのおかげで下記のメリットがある
      - コードは上から下に実行され、宣言的に書ける
      - ロジック部分を明確にわけることができ、一元的に扱える
          - [独自のhooks](https://ja.reactjs.org/docs/hooks-custom.html) (custom hooks)も定義できる
      - 状態を直接扱えるため、HoCやrender propを使う必要がなく、Wrapper hellを避けることができる
          - ライブラリもhooksに対応していればそのライブラリが提供する値や関数を直接扱える

先に紹介したReact Reduxも [hooksを提供しており](https://react-redux.js.org/api/hooks)  `connect()` を使う必要がなく下記のように扱えます。

```jsx
import React from 'react'
import { useSelector } from 'react-redux'

export const CounterComponent = () => {
  const counter = useSelector(state => state.counter)
  return <div>{counter}</div>
}
```

さらなるhooksについての概要はこちらをごらんください 
[フック早わかり](https://ja.reactjs.org/docs/hooks-overview.html)

## hooksの問題点
銀の弾丸は存在しませんが、やはりhooksも同様です。下記のような問題点があります

- Stateが変更されたとき、従来と同じくComponentは再描画される。上から下に実行されるがゆえに、値や関数を必要に応じてメモ化する必要がある。メモ化をうまく扱えないと無限ループを起こしてしまう可能性もある。
- hooksは[制限](https://ja.reactjs.org/docs/hooks-rules.html)があり、いつでもどこでも動かせるわけではない。例えばif文のなかで `useEffect()` は実行できない。
- メモ化などを含め、新たな考え方が必要になるため、hooksは難しいと感じるケースもある

# 注意
Context APIなどここでは紹介しきれてないこともたくさんありますがご容赦ください。

# おわりに
個人的にも今までComponentの設計やHoC、render prop、Wrapper Hellなど様々なことに悩まされてきましたが、hooksを使うことでかなり見通しのよいComponentを定義できるようになったと実感しています。すべてのライブラリがhooksを提供するわけではありませんが、よく使われている主要なライブラリもhooksを提供しはじめてます。hooksを使うにはReactのバージョンアップしないといけないなどの手間もあると思いますが、それを差し引いてもメリットが大きいのでまだ導入してない方はぜひ使ってみてください。
なにか間違ってる箇所とかありましたらツッコミいただけますと幸いです！※特にサンプルコードは作り込む時間ががが

弊社のサービス[Scalebase](https://www.scalebase.com/)でも、絶賛hooksに書き換え中です。興味ある方、お手伝いいただける方はぜひご連絡ください！技術に関するお話だけでも大歓迎です。
twitter: https://twitter.com/mura_cx