Reactのclass componentをHocksに移行してみた

# 概要

Reactで実際自分が使っているclass componentをHocksに移行したらどうなるんだろうと実施。
Material-UIでハマってしまったのでそれをまとめました。
公式ドキュメントだといまいち把握しにくいという人のお役に立てれば。


# 読者対象

- Reactを理解している。
- Hocksに一気に移行しようと意気込んでいる方。
- Contextが好きな方。


# Road to Hocks

## 作業の前に前置き

移行するソースは、Material-UI公式ドキュメントのautocompleteコンポーネントのソースに少しコードを追加したものになります。
https://material-ui.com/demos/autocomplete/#react-select
具体的にはContextとReact Routerの追加、サンプルではシングルとマルチ選択の２つの入力が表示されますが、マルチの方は削除しています。

## import部

![Hocks1.png](https://qiita-image-store.s3.amazonaws.com/0/310578/b52a475b-7664-636e-4403-95add3d59e77.png)


- 左右３行目
    - Hocksの一つであるuseContextを追加インポート。
- 左４行目
    - Material-UIの公式が削除。Hocks導入で不要になったためと思われる。
- 右７行目
    - @material-ui/stylesはまだアルファ版で導入は見送り未使用。(https://material-ui.com/css-in-js/basics/)
    - 試しに導入するとtheme.spacing.unitが見つからないといわれた（公式見るとinstall()実行が必要でそれをしてなかったから？）
- 左右１５行目
    - 私オリジナルのContext。以前はWithItemsContextというHOCでContext.Comsumerタグをラップしていたが、Hocks導入で直接利用。


## class本体部

![Hocks2.png](https://qiita-image-store.s3.amazonaws.com/0/310578/c2dd4536-da44-baab-f37d-8a1f6e9a66ce.png)


- 左右１６５行目
    - Hocks利用の前提条件であるfunction componentへ移行。function化でpropsは引数に。
- 右１６６から１６８行目
    - styleとthemeはHocks移行断念。
- 右１６９行目
    - HocksのuseStateで左１６６から１６９行目に該当。multiは未使用だったので移行で削除。
- 右１７０行目と左１８８行目
    - HOCでpropsにitemsの値渡しをラップしていたが、Hocksの導入のため追加。
- 左１７５から１８５行目と右１７２から１７７行目
    - 以前はhandleChange()がnameという引数を取り、singleかmultiかで状態の保存先を切り替えていたが、個別にFunctionが分かれていたのでSingleのほうだけ採用。
    - Function化した時点ではthis.propsとなっており、propsが見つからないといわれ３０分くらいハマった。Functionの引数のpropsを使うようになるので、```this.```を削除することを忘れずに(;^_^A
- 左１９１行目と右１７９行目
    - 以前はprops.itemsに{ items, getItems() }を入れていたのでitems.itemsとなっていたが、直接扱うようになったのでitemsになったというどうでもいい話。


## 末尾

![Hocks3.png](https://qiita-image-store.s3.amazonaws.com/0/310578/fc2dde5d-bf56-b9cc-2f1b-d2ecdc4ba8bc.png)


- 左２１５から２１６行目と右２０３から２０４行目
    - Hocksの導入でローカル変数になったことと、handleChange()の変更によるもの。
- 左２１８行目
    - 公式のほうで削除されていた。
- 左２２７行目から２３０行目
    - Hocks導入でPropTypesが不要になったと思われる。
- 左２３２行目と右２１２行目
    - Hocks導入でContextのHOCであったWithItemsContextが不要になった
    - 完全に移行できれば```export default withRouter(IntegrationReactSelect);```になったんだが、今回は断念ということでちょっとHOCがまだ多い。


## WithItemsContext(HOC)はどうなっているのか

Contextからitemsを取得して、ラップ先のpropsに渡してあげているだけです。
記憶通りなら公式ドキュメントからほぼコピペだったはず。

```javascript:WithItemsContext.js

import React from 'react';

import { ItemsContext } from '../../contexts/items'

function withItemsContext(WrappedComponent) {
    return class extends React.Component {
        constructor(props) {
            super(props);
            // this.handleChange = this.handleChange.bind(this);
            this.state = {
                items: [],
            };
        }

        render() {
            return (
                <ItemsContext.Consumer>
                    {items => (
                        <WrappedComponent items={items} {...this.props} />
                    )}
                </ItemsContext.Consumer>
            );
        }
    };
}

export default withItemsContext;
```


# まとめ

class componentを一気にHocks対応させようとしたら、Material-UIの部分で躓いてしまった。
Hocksを利用するにはfunction componentへの移行が必須のため、ステートレスならfunction化と段階を踏んだほうがいい。
また、導入パッケージの対応状況（今回の場合Matarial-UIとReact Router）も確認しておきたいところ。
しっかり事前準備をして、公式も言う通り段階的な移行がベスト。
私のケースでも時間をたってからであればスムーズな移行ができたはず。

今回の移行理由はContextが増えるたびにHOC作るの面倒だと思ったこと。
増産するのは簡単だが、テンプレソースを作らないに越したことはない。
また、HOCだとラップしてしまうためにitemsの出所が不明だが、Hocks使うとContextでどのContextか一目瞭然。
stateの方は必要に応じてみる予定ですが、これも結局ロジックを外に出せることが主なメリットの一つ？

ちなみに私はフロント男子ではないので、Matarial-UIのサンプルコードの移行具合に完全に依存します(;^_^A
私がTSやReduxを導入していない理由・・・それは公式コードをほぼそのまま利用できることに他ならない(;^ω^)