React 公式チュートリアルをフックで書き換える

いまさらですが [React Hooks](https://ja.reactjs.org/docs/hooks-intro.html) を勉強したので, 素振りのために React の公式チュートリアルをフックを使って書き換えてみようと思いました. この記事はその備忘録です.

## 準備

1. React 公式ガイドの Main Concepts を [第 1 章 Hello World](https://ja.reactjs.org/docs/hello-world.html) から [第 12 章 React の流儀](https://ja.reactjs.org/docs/thinking-in-react.html) まで読んでいき, React に入門します.

1. [静的型チェック](https://ja.reactjs.org/docs/static-type-checking.html) や [React+TypeScript Cheatsheets](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet) を読み, React + TypeScript 環境の構成方法を学びます.

1. [チュートリアル](https://ja.reactjs.org/tutorial/tutorial.html)を一通り終えます. ただし僕はここでは CodePen 上で試すのではなくローカルで TypeScript も使いながら進めていくことにしたので, 最初に実行するコマンドは以下となります:

```console
npx create-react-app rewrite-react-tutorial-in-hooks --typescript
```

## React Hooks で書き換える

準備が整ったら本題の React Hooks による書き換えを行おうと思いますが, その前に公式ドキュメントの [1. フックの導入](https://ja.reactjs.org/docs/hooks-intro.html) の中にある [クラスは人間と機械の両方を混乱させる](https://ja.reactjs.org/docs/hooks-intro.html#classes-confuse-both-people-and-machines) という節を読むと, 以下のように書かれています:

> コードの再利用や整頓が難しくなるということに加えてクラスについて我々が学んだことは、クラスが React を学ぶ上で障壁となっているということです。
> (中略) これらの問題を解決するため、フックは、より多くの React の機能をクラスを使わずに利用できるようにします。コンセプト的には、React のコンポーネントは常に関数に近いものでした。

そういうわけで, React の世界観においてはクラスよりも関数コンポーネントのほうが好まれるようです.

そこでチュートリアルが終わった段階のコードをあらためてよく眺めてみると, `Board` コンポーネントがもはや `state` を持たなくなっていることがわかります (`Game` コンポーネントにリフトアップしたので).
なので, これはフックと直接の関係はありませんが, `Board` コンポーネントを pure function として書き直しておきましょう:

```diff
-class Board extends React.Component<BoardProps> {
-  renderSquare(i: number) {
+const Board = (props: BoardProps) => {
+  const renderSquare = (i: number) => {
     return (
       <Square
-        value={ this.props.squares[i] }
-        onClick={ () => this.props.onClick(i) }
+        value={ props.squares[i] }
+        onClick={ () => props.onClick(i) }
       />
     )
   }
-  render() {
-    return (
-      <div>
-        <div className="board-row">
-          {this.renderSquare(0)}
-          {this.renderSquare(1)}
-          {this.renderSquare(2)}
-        </div>
-        <div className="board-row">
-          {this.renderSquare(3)}
-          {this.renderSquare(4)}
-          {this.renderSquare(5)}
-        </div>
-        <div className="board-row">
-          {this.renderSquare(6)}
-          {this.renderSquare(7)}
-          {this.renderSquare(8)}
-        </div>
+  return (
+    <div>
+      <div className="board-row">
+        {renderSquare(0)}
+        {renderSquare(1)}
+        {renderSquare(2)}
       </div>
-    );
-  }
+      <div className="board-row">
+        {renderSquare(3)}
+        {renderSquare(4)}
+        {renderSquare(5)}
+      </div>
+      <div className="board-row">
+        {renderSquare(6)}
+        {renderSquare(7)}
+        {renderSquare(8)}
+      </div>
+    </div>
+  )

```

そうしたらフックの公式ドキュメントを [8. フックに関するよくある質問](https://ja.reactjs.org/docs/hooks-faq.html) まで読み, フックを完全理解しましょう.

### `useState` フック

さて, フックを使うための第一歩は, クラスのコンストラクタ内で `this.state` を初期化するのをやめて `useState` フックを使うようにするということです:

```diff
-import React from 'react';
+import React, { useState } from 'react';
```

```diff
-class Game extends React.Component<any, GameState> {
-  constructor(props: any) {
-    super(props)
-    this.state = {
-      history: [{
-        squares: Array(9).fill(null)
-      }],
-      stepNumber: 0,
-      xIsNext: true
-    }
-  }
+const Game = () => {
+  const [history, setHistory] = useState([{
+    squares: Array(9).fill(null)
+  }])
+  const [stepNumber, setStepNumber] = useState(0)
+  const [xIsNext, setXIsNext] = useState(true)
```

`this.state` が存在しなくなったので, `this.state.history` みたいな形で状態にアクセスするのもやめて `history` 等を直接読み出すようにします.

### `setState`

`this.setState` で状態を更新していた箇所は `useState` の返り値であるところの `setHistory` 等で書き換えます.

### プロトタイプメソッド

クラスの中にあった `handleClick` 等のメソッドは, もはやクラスが存在しなくなるので `const handleClick = () => {}` のような形で関数宣言するよう変更します.
呼び出すときは `this` 経由ではなく直接利用するようにします.

### `render`

JSX は `render` 関数が返すのではなく, 関数コンポーネントから直接 `return` するようにします.

### チュートリアルにおける書き換えの注意

チュートリアルのコードでは `useEffect` が必要になる箇所は無いので, 上記までの作法に従えばおおよそ書き換えは完了するでしょう. 具体例としては `jumpTo` がわかりやすいかもしれません:

```diff
-  jumpTo(step: number) {
-    this.setState({
-      stepNumber: step,
-      xIsNext: (step % 2) === 0
-    })
+  const jumpTo = (step: number) => {
+    setStepNumber(step)
+    setXIsNext((step % 2) === 0)
   }
```

唯一の注意点は, `handleClick` の中の

```js
const history = this.state.history.slice(0, stepNumber + 1)
```

という部分は `history` という変数名がかぶっているので,

```js
const historyUntilCurrentStep = history.slice(0, stepNumber + 1)
```

のように別名の変数を使う必要があったということです.

### React.useState で型を明示的に指定できる

[React+TypeScript Cheatsheets](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet) の [Hooks](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#hooks) の節にあるように, `React.useState` (とそのエイリアスである `useState`) による型推論は多くの場合非常にうまくいきます. しかしチュートリアルの中で `history` の型だけは少し複雑なので, 推論がうまくいかない場合があるかもしれません. そのようなときのために `React.useState` では以下のように明示的に型を指定することができます:


```react
const [history, setHistory] = React.useState<Array<{
  squares: Array<string | null>
}>>([{
  squares: Array(9).fill(null)
}])
```

## 駆け足でしたが以上です

チュートリアルの範囲内だと `useEffect` を使う必要がなかったので, フックを使ううまみはそこまで出ていないかもしれません.
それでもコードの中から `this` が消え去ったので, いつ `this` を使うべきか気を使わずに済むようになったぶん脳にかかる負荷が軽くなり, 素朴で素直な JavaScript になったと思います.

これは個人的な感想ですが, React 自体も公式ドキュメントもなるべく JavaScript 的に素直な流れで読めるように, 認知負荷を下げる方向に進むよう配慮されていて, 心地良い世界であるなあと現時点では感じています.

(おしまい)