🎉React 16.8: 正式版となったReact Hooksを今さら総ざらいする

来たる 2 月 4 日、ついに React 16.8 の正式版がリリースされます。この React 16.8 には、アルファ版が公開されて以来常に React ユーザーたちの関心をほしいままにしてきた**React Hooks**が正式版となって追加されます。

<font color="#777777">※追記：<del>アメリカ時間で 2 月 5 日になっても React 16.8 がリリースされませんでした。事前情報で 2 月 4 日と言ってたのに……。いつになったらリリースされるのかはよく分かりません。</del>2 月 6 日に React 16.8 がリリースされました！</font>

熱心な React ユーザーの方は、当然 React Hooks の情報を常に追っており正式版がリリースされたらすぐにでも自分のコードで使いはじめる準備ができていることと思います。しかし、この記事をご覧の方々の中には React を使っているにも関わらず「**React Hooks のことはよく分からない**」とか「**聞いたことくらいはあるけど今どうなっているのか知らない**」とか「**正式版が出てから調べればいいやと思っていた**」という方がもしかしたら居るかもしれません。

いよいよ正式版の登場と相成ってしまいましたから、皆さんはもう React Hooks から逃げることはできません[^note1]。この記事では上記のような方々を対象として、**React Hooks の正式版に備えてどんなフックがあるのかをすべて説明します**。アルファ版のうちは API の変化もありましたから、少し前にちょっと調べてみたけど今どうなっているのかは分からないというような人は要チェックです。なお、React はドキュメントの充実に力を入れており、React Hooks に関しても[公式ドキュメント](https://reactjs.org/docs/hooks-reference.html)がちゃんと最新の状態に保たれています。ですから、英語が読める人はこの記事を読まずに公式ドキュメントを読むのもよいかもしれません。

[^note1]: 一応補足しておくと、クラスコンポーネントなどの旧来の機能が React Hooks に取って代わられて廃止されるという予定は今のところありません。ですから、React Hooks を避けながら React を使い続けることも可能です。筆者はそういう人（/チーム）は React を使うのに向いていないという説を推しますが。

記事中に登場するサンプルコードは[GitHub](https://github.com/uhyo/react-hooks-sandbox/tree/review/src/review)に置いてあります。

# React Hooks の概要

総ざらいというタイトルをつけた記事なので、一応 React Hooks について基礎から説明しておきます。React Hooks は、一言で言えば**関数コンポーネントから使える新しい API**です。これまで、関数コンポーネントは引数として props を受け取り、レンダリング結果を返り値で返すというインターフェースで定義され、ステートなどの複雑な機能を持たない単純なコンポーネントを作れるものとして提供されていました。初期には Stateless Function Component（状態なし関数コンポーネント）という用語が使われていたことからも分かる通り、もともと関数コンポーネントは、クラスコンポーネントが持つような`state`の機能や`componentDidMount`を始めとするライフサイクルメソッドを持ちませんでした。

React Hooks によって、関数コンポーネントにこれらの機能を持たせることができるようになります。すなわち、従来はクラスコンポーネントでしか出来なかったことが関数コンポーネントでもできるようになるのです。ただ一つ注意していただきたいのは、**React Hooks はクラスコンポーネントの API をただ移植したわけではない**ということです。React Hooks の API は新しくデザインし直された API であり、従来の API の問題点が解消されています。例えば、フックの利用方法をただの関数呼び出しとすることによって、フックの合成（＝カスタムフックの作成）を容易にしています（カスタムフックについては[筆者の以前の記事](https://qiita.com/uhyo/items/27a83d9044945034c4da)が参考になるかもしれません）。

React Hooks は、API 上は`use`から始まる名前のただの関数です。`react`パッケージから`useState`や`useEffect`などの関数がエクスポートされています。裏では React が今どのコンポーネントを処理しているかといった情報を保持しており、これらの関数はその情報に基づいて適切に動作します。現在のところ、React Hooks の API は関数コンポーネント専用であり、それ以外のシチュエーションで呼び出すことはできません。

# 基本のフック

先述のドキュメントでは、`useState`, `useEffect`, `useContext`の 3 つが基本的なフックとして挙げられています。それに倣ってここでもまずはこの 3 つを紹介します。

## useState

`useState`フックは、**関数コンポーネントにステートを持たせられる API** です。このフックを使うと、クラスコンポーネントにおける`this.state`に値を保存しておくのと同じような感じで、コンポーネントに状態を持たせることができます。使い方は`const [stateの値, state更新関数] = useState(state初期値);`というのが基本です。

### useState のサンプル

さっそくですが、`useState`の使用例を見てみましょう。よくある例ですが、ボタンを押すと数値が増えたり減ったりするやつです。

```jsx:useStateのサンプル
import * as React from 'react';
const { useState } = React;

export const UseStateSample = () => {
  const [count, setCount] = useState(0);

  return (
    <p>
      <button onClick={() => setCount(count - 1)}>-</button>
      <b>{count}</b>
      <button onClick={() => setCount(count + 1)}>+</button>
    </p>
  );
};
```

この`useStateSample`は関数コンポーネントで、特に props を受け取らずに render 内容を返すという従来どおりのインターフェースを持っています。ポイントは、その処理の中で`useState`関数を呼び出している点です。

今回は`count`というステートを初期値`0`で用意しています。よって、`<b>{count}</b>`のところは最初は 0 が表示されます。2 つのボタンをクリックすると、`setCount`が呼び出されるようになっています。`setCount`を呼び出した場合は渡した引数で`count`ステートが更新されます。つまり、`UseStateSample`の再`render`処理が発生し、その際`useState`によって返される`count`の値が新しい値となっています。

### クラスコンポーネントとの比較

上のサンプルと同じ処理を敢えて旧来のクラスコンポーネントで書くとこんな感じです。

```jsx
export class UseStateSample extends React.Component {
  state = { count: 0 };
  render() {
    const { count } = this.state;
    return (
      <p>
        <button onClick={() => this.setState({ count: count - 1 })}>-</button>
        <b>{count}</b>
        <button onClick={() => this.setState({ count: count + 1 })}>+</button>
      </p>
    );
  }
}
```

関数コンポーネントと Hooks で書いた場合に比べて煩雑ですね。その要因としては、クラスコンポーネントでは`this.state`がオブジェクトであることや、`this.setState`がそのオブジェクトのうち一部をアップデートするような API 設計になっていることが挙げられます。React Hooks の API では`count`というステートに対して`setCount`という専用の関数がセットで用意されますから、`{count: count+1}`のような余計なオブジェクト生成がなく直感的かつすっきりと書くことができています。

### 複数のステートを使う

関数コンポーネントでは、`useState`を複数回呼ぶことで複数のステートを利用することができます。その場合、それぞれのステートに対して別々の更新関数が得られます。

```jsx:複数のステートを用いる例
export const UseStateSample2 = () => {
  const [left, setLeft] = useState(0);
  const [right, setRight] = useState(0);

  return (
    <p>
      <b>{left}</b>
      <button
        onClick={() => {
          setLeft(left + 1);
          setRight(right - 1);
        }}
      >
        ←
      </button>
      <b>{right}</b>
      <button onClick={() => setRight(right + 1)}>+</button>
    </p>
  );
};
```

この例ではコンポーネントは`left`と`right`という 2 つのステートを持っています。それぞれのステートをアップデートするには対応する関数を用います。真ん中あたりに見えるように、複数のステートを同時に更新するには関数を全部呼びます。

これを見ると、ステートが複雑になってきたときにステートの更新が一発でできずにたくさんの関数呼び出しが必要になってしまうという懸念があるかもしれません。その場合はステートの値を数値などではなくオブジェクトにして一発で更新するほうがよいことがあります。このやり方を支援するフックとして`useReducer`がありますので、あとで紹介します。

### 関数によるステートの更新

ステートの更新関数（上の例での`setCount`など）を呼ぶ場合、今までのサンプルでは新しいステートの値を渡していました。下記の例では、このボタンを押すと`count`ステートを 1 増やす、つまり新しい値として`count + 1`をセットしていることが分かります。

```jsx
<button onClick={() => setCount(count + 1)}>+</button>
```

実は、更新関数には**関数**を渡すことができます。その場合、これは現在のステートの値を受け取って新しいステートの値を返す関数として解釈されます。つまり、上の例はこのように書き換えることができます。

```jsx
<button onClick={() => setCount(count => count + 1)}>+</button>
```

これは`setCount`に関数を渡すことで、「現在のステートを読んで、それに 1 を足した値を新しいステートの値とする」ということを指示しています。このように、次のステートの値が現在のステートに依存する場合（すなわち現在のステートから新しいステートを計算する場合）は関数を用いた更新が適しています。

その理由の 1 つは、この方法だとステートの更新ロジックを純粋関数に抜き出すことができることです。さらにもう 1 つの理由として、関数を使わないとコールバックが複数回呼ばれる場合に正しく対処できないことがあります。

例として、「1 回押すと onClick が 5 回発生するボタン」というコンポーネント`SuperButton`を作ってみました。このボタンを使うと 2 つの方法の違いが分かります。

```jsx
const SuperButton = ({ onClick, children }) => {
  const onclickHere =
    onClick &&
    (e => {
      for (const _ of [0, 1, 2, 3, 4]) onClick(e);
    });
  return <button onClick={onclickHere}>{children}</button>;
};
export const UseStateSample4 = () => {
  const [count, setCount] = useState(0);

  return (
    <p>
      <SuperButton onClick={() => setCount(count - 1)}>-</SuperButton>
      <b>{count}</b>
      <SuperButton onClick={() => setCount(count => count + 1)}>+</SuperButton>
    </p>
  );
};
```

この例では、`SuperButton`の onClick 関数で 2 種類の方法で書いたステートの更新を発生させています。既にお察しかと思いますが、`() => setCount(count - 1)`のほうは、これが 5 回連続で呼び出されても`count`は 1 だけ減ります。なぜなら、`setCount(count - 1)`で参照される`count`は常にこの`UseStateSample4`コンポーネントがレンダリングされたときの`count`だからです。
それに対し、`()=> setCount(count => count + 1)`の場合は、これが 5 回呼び出されることで`count`は 5 増えます。これは、この関数が呼び出されるたびに現在の`count`の値が参照される（そしてそれに 1 を足した数値が新しい`count`となる）からです。

このように、関数がワンパスで複数回呼び出されることを想定すると、関数を用いたステート更新（上の例で言えば後者）にしないと想定した動作にならないことがあります。繰り返しますが、現在のステートに依存して更新を行うならこのように関数をステート更新関数に渡しましょう。

## useEffect

`useState`と並んでよく使うであろうフックがこの`useEffect`フックです。これは**レンダリング後に行う処理を指定できるフック**です。クラスコンポーネントのライフサイクルでいえば、`componentDidMount`及び`componentDidUpdate`に**おおよそ**相当するものです（実際には多少違うのですが、それは後で説明します）。

例えば、以下の例は 1 秒ごとに表示されている値が 1 増えるコンポーネントです。現在表示している値をステートで管理するために、さっき紹介した`useState`と組み合わせて実装しています。

```jsx
import * as React from 'react';
const { useState, useEffect } = React;

export const UseEffectSample1 = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timerId = setTimeout(() => {
      setCount(count => count + 1);
    }, 1000);
    // クリーンアップ関数を返す
    return () => clearTimeout(timerId);
  }, [count]);

  return (
    <p>
      time: <b>{count}</b>
    </p>
  );
};
```

このように、`useEffect`は 2 つの引数を取ります。返り値はありません。また、2 つ目の引数は省略可能です。1 つ目の引数は**コールバック関数**であり、**この関数はコンポーネントのレンダリング完了時に呼ばれます**。最初のレンダリングはもちろん、再レンダリングが発生したあとにも呼ばれます。ただし、第 2 引数でコールバック関数を呼ぶタイミングを制御できます。第 2 引数は値の配列であり、**配列のいずれかの値が前回と変わったときのみコールバック関数を呼ぶ**という意味になります。省略した場合は無条件、つまりレンダリングの度に関数が呼ばれます[^useeffect_note1]。

[^useeffect_note1]: 第 2 引数の省略と、第 2 引数に`[]`を指定するのとは異なるという点に注意してください。第 2 引数を省略した場合はレンダリングごとにコールバック関数が呼ばれますが、`[]`を指定した場合は初回のレンダリングでのみコールバック関数が呼ばれます。

上のサンプルでは、まず最初のレンダリングが完了したタイミングで`useEffect`に指定したコールバック関数が呼びだされます。それにより、1 秒後に`count`ステートが更新されます。それによりコンポーネントの再レンダリングが発生し、再び`useEffect`のコールバック関数が呼ばれます。この繰り返しにより、このコンポーネントは 1 秒ごとに数値が 1 ずつ増えていくコンポーネントとなります。

今回第 2 引数は`[count]`です。つまり、レンダリング終了時に、`count`の値が前回のレンダリングと変わっていたら`useEffect`のコールバックを発火するということになります。今回の場合はステートがこれだけなので省略しても同じですが、他にもステートがある場合に余計な処理が発生しないようにする効果があります。

ここで呼ばれているコールバック関数は、よく見ると関数を戻り値としています。これは**クリーンアップ関数**です。クリーンアップ関数を宣言した場合は、**次回のコールバックが呼ばれる前にクリーンアップ関数が呼ばれます**。また、**コンポーネントがアンマウントされる前にもクリーンアップ関数が呼ばれます**。要するに、描画時にコールバック関数が呼ばれた場合、その描画が消されるときに対応するクリーンアップ関数が呼ばれるということです。とても便利ですね。

上の例では、正確には初回レンダリング →`useEffect`のコールバック関数が呼ばれる →1 秒後にステートが変更されて再レンダリングが発生 →**クリーンアップ関数が呼ばれる**→`useEffect`のコールバック関数が再び呼ばれる　という流れをとっていることになります。ここではクリーンアップ関数で`clearTimeout`を呼んでいますが、このようにコールバック関数で発生した副作用の後始末をするのがクリーンアップ関数の主な役目です。このクリーンアップ関数は`setTimeout`の発火後に呼ばれた場合は意味がありませんが、コンポーネントがアンマウントされてまだ`setTimeout`が発火していない場合に、すでにアンマウントされたコンポーネントのステートを変更してしまうのを防ぐ意味があります。

## useContext

`useContext`は、**指定したコンテキストの現在の値を得るフック**です。コンテキストというのは React 16.3 で搭載された新しいコンテキスト API によるものを指しています。`React.createContext`で作成したコンテキストオブジェクトを`useContext`に渡すことで、そのコンテキストの現在の値を返り値で得ることができます。

```jsx:useContextのサンプル
import * as React from 'react';
const { useState, useContext, createContext } = React;

const MyContext = createContext(() => {});

export const UseContextSample = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>
        <b>{count}</b>
      </p>
      <MyContext.Provider value={() => setCount(count => count + 1)}>
        <IncrementButton />
      </MyContext.Provider>
    </div>
  );
};

const IncrementButton = () => {
  const incrementHandler = useContext(MyContext);

  return (
    <p>
      <button onClick={incrementHandler}>+</button>
    </p>
  );
};
```

この例は相変わらずボタンを押すと数値が増えるという単純なサンプルですが、数値を増やすボタンを`IncrementButton`という別のコンポーネントに移しました。そうなると、ボタンを押したときの処理（`UseContextSample`のステートを変更する）を`IncrementButton`コンポーネントに伝える必要があります。一番単純な方法は props のひとつとしてその関数を渡すことですが、何段階も伝播させないといけないような場合には props を使うのは適していないことがあります。その場合はこの例のようにコンテキストを使って暗黙に値を伝播させます。

`IncrementButton`を従来の API で書き直すとこんな感じです。コンポーネントのネストが無くなって綺麗に書けるのが嬉しいですね。

```jsx
const IncrementButton = () => {
  return (
    <MyContext.Consumer>
      {incrementHandler => (
        <p>
          <button onClick={incrementHandler}>+</button>
        </p>
      )}
    </MyContext.Consumer>
  );
};
```

# そのほかのフック

ここまでの 3 つがとても良く使いそうなフックでした。それ以外のフックも続けて紹介していきます。比較的よく使いそうなフックから解説していきます。

## useReducer

`useReducer`は **`useState` の亜種**であり、ステートを宣言するフックです。`useReducer`は、ステートの初期状態に加えて**reducer と呼ばれる関数を渡します**。結果として、現在のステートに加えて、**ステートを更新する関数の代わりに dispatch 関数が得られます**。この reducer や dispatch というのは Redux 用語としてよく知られていますので、Redux を触ったことがあるかたは馴染み深い概念でしょう。

`useReducer`によって作られたステートを更新する場合は、`dispatch`関数に**アクション**を渡します。アクションというのはステートの更新指示を表す値で、別になんでも構いません。そのアクションを引数として、`useReducer`に渡した reducer が呼び出されます。reducer というのは関数であり、アクションと現在のステートを受け取って新しいステートを返します。

```jsx:useReducerの例
import * as React from 'react';
const { useContext, useReducer, createContext } = React;

const DispatchContext = createContext(() => {});

const reducer = ({ year, month }, action) => {
  switch (action) {
    case 'increment':
      return month === 11
        ? { year: year + 1, month: 0 }
        : { year, month: month + 1 };
    case 'decrement':
      return month === 0
        ? { year: year - 1, month: 11 }
        : { year, month: month - 1 };
  }
};

export const UseReducerSample = () => {
  const [state, dispatch] = useReducer(reducer, {
    year: 0,
    month: 1,
  });

  return (
    <div>
      <p>
        <b>
          {state.year}年{state.month}ヶ月
        </b>
      </p>
      <DispatchContext.Provider value={dispatch}>
        <ControlButtons />
      </DispatchContext.Provider>
    </div>
  );
};

const ControlButtons = () => {
  const dispatch = useContext(DispatchContext);

  return (
    <p>
      <button onClick={() => dispatch('decrement')}>-</button>
      <button onClick={() => dispatch('increment')}>+</button>
    </p>
  );
};
```

この例はこのようにレンダリングされます。

[![Image from Gyazo](https://i.gyazo.com/f31fbc5d6970c97e49e7ef79493774f2.png)](https://gyazo.com/f31fbc5d6970c97e49e7ef79493774f2)

この例のように、**useReducer**には 2 つの引数を渡します。1 つ目の引数は reducer で、2 つ目の引数は初期状態です。今回`useReducer`で宣言するステートは`year`と`month`という 2 つのプロパティを持ったオブジェクトです。`+`ボタンと`-`ボタンを押すと、月が 1 つ増えます。12 ヶ月になると年が 1 つ増えて月が 0 に戻ります。（あくまでサンプルなので、年とか 12 で割って計算しろよという批判は受け付けません。）

ボタンが押されたときのロジックが`reducer`関数にまとまっていることが分かりますね。`reducer`の第 1 引数が今の状態で、第 2 引数がアクション（単純な例なので今回は文字列）です。そして、`useReducer`により宣言されたステートの更新は、`dispatch`関数にアクションを渡すことで行います。`dispatch`を子コンポーネントである`ControlButtons`に渡すのは先ほど紹介した`useContext`を使いました。

今回のポイントは、`+`ボタンを押すと`year`と`month`が両方同時に変化する可能性があるということです。このような複雑な変化をアクションという抽象的な命令で表すことによって、ステートを変化させる側（`dispatch`を呼び出す側）の単純化とステート変更ロジックの分離を同時に達成しています。`useReducer`を用いて色々な状態をひとまとめに宣言する場合、色々な子コンポーネントが`dispatch`を用いて状態を変化させるはずですから、この例のように`useContext`と組み合わせて`dispatch`関数を子コンポーネントたちに伝えるのが特に適しています。ステートの更新はすべて`dispatch`を通じて行うため、`dispatch`関数ひとつ伝えれば十分であるというのも嬉しい点です。

### 関数によるステートの初期化

`useReducer`の第 2 引数に初期ステートを渡す代わりに、**関数**を用いてステートを初期化することができます。この場合、`useReducer`の第 3 引数に初期化関数を渡します。また、第 2 引数の意味が変わります。渡した初期化関数の引数として`useReducer`第 2 引数が渡されます。この機能は、ステートの初期化ロジックを別の関数として定義したい場合などに便利です。

```jsx:useReducerの第3引数の例
const reducer = ({ year, month }, action) => {
  switch (action) {
    case 'increment':
      return month === 11
        ? { year: year + 1, month: 0 }
        : { year, month: month + 1 };
    case 'decrement':
      return month === 0
        ? { year: year - 1, month: 11 }
        : { year, month: month - 1 };
  }
};

const init = initialMonth => ({
  year: 0,
  month: initialMonth,
});

export const UseReducerSample2 = ({ initialMonth }) => {
  const [state, dispatch] = useReducer(reducer, initialMonth, init);

  return (
    <div>
      <p>
        <b>
          {state.year}年{state.month}ヶ月
        </b>
      </p>
      <DispatchContext.Provider value={dispatch}>
        <ControlButtons />
      </DispatchContext.Provider>
    </div>
  );
};
```

この例では、ステートの初期化関数`init`を定義して`useReducer`の第 3 引数に渡しました。このコンポーネントを`<UseReducerSample2 initialMonth={10} />`のように使うと、「0 年 10 ヶ月」の表示からスタートします。このように、第 2 引数を初期ステートではなくステートの元となる値にして、第 3 引数に渡した`init`関数でそれをステートに変換するという方式をとっています。

この場合は第 2 引数を`{year: 0, month: initialMonth}`とするという手もありますが、初期ステートがコンポーネントの中で定義されているのはどうも微妙に思えます。そういう時に第 3 引数を使いましょう。

### useReducerについてさらに詳しく理解する。

`useReducer`がもたらす恩恵についてさらに詳しく書いた記事を用意しました。React Hooksに少し慣れたころに読むのが丁度いいかもしれません。

- [useReducerの本質：良いパフォーマンスのためのロジックとコンポーネント設計](https://qiita.com/uhyo/items/cea1bd157453a85feebf)

## useMemo

`useMemo`は、その名の通り**値のメモ化に使えるフック**です。第 1 引数に値を計算する関数を、第 2 引数に計算される値が依存する値の一覧を渡します。

```jsx:useMemoの例
import * as React from 'react';
const { useMemo } = React;

export const UseMemoSample = ({ n }) => {
  const sum = useMemo(() => {
    let result = 0;
    for (let i = 1; i <= n; i++) {
      result += i;
    }
    return result;
  }, [n]);

  return (
    <div>
      <p>
        1 + … + n = <b>{sum}</b>
      </p>
    </div>
  );
};
```

ここで定義した`UseMemoSample`は、1 から指定した値までの和を表示するという意味不明なコンポーネントです。しかも、その和は for ループを回して計算します。`<UseMemoSample n={1000} />`のように使うと`1 + … + 1000 = 500500`と表示します。

この例はともかく、ループとか回す処理を render の中にベタ書きすると、レンダリングが行われるたびにそれが計算されることになります。計算結果をメモ化したい、すなわち以前計算した結果が再利用できるときは再利用したい、という場合に`useMemo`が役に立ちます。

`useMemo`の第 1 引数は、値を計算する関数を渡します。それ自身に引数はありませんが、props の値などを使用しても構いません。その関数の返り値が`useMemo`の返り値となります。この関数が呼び出されるのは値の計算が必要となったときです。つまり、初回のレンダリング時および再計算が必要となったときに関数が呼び出されます。`useMemo`は以前の計算の結果を覚えており、再計算が必要ない場合は渡した関数は呼び出されず、以前の計算の結果が返されます。

再計算がいつ必要かは`useMemo`の第 2 引数で指定します。これは`useEffect`の第 2 引数と同じで、ここに渡した値のいずれかが変化したときに再計算が行なわれます。今回の場合は計算結果は`n`に依存しているため、第 2 引数に`[n]`を渡す必要があります。これにより、`n`が変化したとき再計算が行なわれるようになります。

## useCallback

`useCallback`は`useMemo`の亜種です。簡潔に言えば、`useCallback(fn, arr)`は`useMemo(()=> fn, arr)`と同じです。つまり、`useCallback`は計算の必要ない値をメモ化するときに便利なフックです。

計算が必要ないのにメモ化とはどういうことかとお思いかもしれませんが、`useCallback`という名前が示唆するとおり、これは**関数をメモ化するのに便利**です。ポイントは、**`()=> { ... }`のような関数式は毎回新しい関数オブジェクトを作る**という点です。

以下に`useContext`のサンプルを再掲します。

```jsx
export const UseContextSample = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>
        <b>{count}</b>
      </p>
      <MyContext.Provider value={() => setCount(count => count + 1)}>
        <IncrementButton />
      </MyContext.Provider>
    </div>
  );
};
```

ここで`MyContext.Provider`の prop として`()=> setCount(count => count+1)`という関数を渡しています。これは関数式なので、`UseContextSample`がレンダリングされるたびに新しい関数オブジェクトが作られてそれが`MyContext.Provider`に渡されます。実はこれは良くありません。なぜなら、コンテキストに渡された値が変化するたびにそのコンテキストの値を使用するコンポーネントは全部再描画されるため、上の例で`MyContext`を使用するコンポーネントが毎回再描画されてしまうからです。

こういう時は`useCallback`の出番です。以下のようにすることで再描画を防ぐことができます。

```jsx:useCallbackのサンプル
import * as React from 'react';
const { useState, useContext, useCallback, createContext } = React;

const MyContext = createContext(() => {});

export const UseCallbackSample = () => {
  const [count, setCount] = useState(0);

  const updateCount = useCallback(() => setCount(count => count + 1), []);

  return (
    <div>
      <p>
        <b>{count}</b>
      </p>
      <MyContext.Provider value={updateCount}>
        <IncrementButton />
      </MyContext.Provider>
    </div>
  );
};

const IncrementButton = React.memo(() => {
  const incrementHandler = useContext(MyContext);

  return (
    <p>
      <button onClick={incrementHandler}>+</button>
    </p>
  );
});
```

この例では、`MyContext.Provider`に渡す関数を`useCallback`を用いてメモ化しています。第 2 引数が`[]`ということは、一度初期化された後は`updateCount`は毎回同一の関数オブジェクトとなります。よって、`MyContext.Provider`の値は変化していない扱いとなり、`IncrementButton`は再描画されなくなります。

ただし、`IncrementButton`が`React.memo`で囲うように変更されている点に注意してください。これはフックではないので詳細は省きますが、親の`UseCallbackSample`が再描画されても自動的に`IncrementButton`が再描画しないようにする効果があります。この 2 つの施策により`IncrementButton`が再描画される要因が無くなります。このように`useCallback`（や`useMemo`）は最適化に利用することができます。

## useRef

`useRef`は、**ref オブジェクトを作って返すフック**です。この ref オブジェクトは`React.createRef`を使って作ることができるオブジェクトのことです。`useRef`は、同じ呼び出しに対しては同じ ref オブジェクトを返します。ref オブジェクトは`current`プロパティにその中身が入っています。`current`の初期値は`useRef`の引数で指定することができます。

`useRef`のひとつの用途は、コンポーネントの`ref`属性に渡すためのrefオブジェクトを作ることです。`useEffect`と組み合わせた例を作ってみました。

```jsx:useRefのサンプル
import * as React from 'react';
const { useEffect, useRef } = React;

export const UseRefSample = () => {
  const displayAreaRef = useRef();

  useEffect(() => {
    let rafid = null;
    const loop = () => {
      // 現在時刻を表示
      const now = new Date();
      displayAreaRef.current.textContent = `${String(now.getHours()).padStart(
        2,
        '0',
      )}:${String(now.getMinutes()).padStart(2, '0')}:${String(
        now.getSeconds(),
      ).padStart(2, '0')}.${String(now.getMilliseconds()).padStart(3, '0')}`;
      rafid = requestAnimationFrame(loop);
    };
    loop();
    return () => cancelAnimationFrame(rafid);
  });

  return <p ref={displayAreaRef} />;
};
```

このコンポーネントはこのような表示となります。

[![Image from Gyazo](https://i.gyazo.com/e4680d4d4038e26962d72a0fe09cd619.png)](https://gyazo.com/e4680d4d4038e26962d72a0fe09cd619)

これは、`requestAnimationFrame`を用いてリアルタイムで現在時刻をミリ秒単位で表示するコンポーネントです。このような高頻度な更新をステートを用いて行うのは負荷が高そうな気がするので`useEffect`を用いて直接 DOM 操作を行っています。

直接 DOM 操作を行うには、レンダリング後に生の DOM ノードを取得する方法があります。その方法がコンポーネントの`ref`属性（`<p ref={displayAreaRef} />`）です。この`ref`属性に渡すための ref オブジェクトを`useRef()`フックで作っています。`ref`属性の効果により、レンダリング時に ref オブジェクトの`current`プロパティに DOM ノードがセットされます。この例では`useEffect`のハンドラの中からこの情報を使用しています。

### レンダリング間変数として useRef を使用する

従来、ref オブジェクトというのは専ら、上記の例のように DOM オブジェクトへの参照を得るのに使われてきました。しかし、フックの世界においては`useRef`や ref オブジェクトはその便利さをさらに増しています。
ポイントは、`useRef`が返すオブジェクトはレンダリング間で毎回同じであるということです。特に`useEffect`を使用する場合に、レンダリング間で情報を共有する場合に使用可能です。

例えば、`componentDidUpdate`は「前回の props」と「前回の state」を参照可能でしたが`useEffect`にはその機能はありませんでした。前回の props などを利用したい場合は、その情報を自分で`useRef`に保存しておくことになります。

以下の例は、自分が再レンダリングされた回数を覚えているコンポーネントです。（本当はこういう用途で`useEffect`を使わずにロジックで何とかしたほうがよいですが。というか再レンダリングされた回数が必要なのってどんな場面なのでしょうか。）

```jsx
export const UseRefSample2 = () => {
  const displayAreaRef = useRef();
  const renderCountRef = useRef(0);

  useEffect(() => {
    renderCountRef.current++;
    displayAreaRef.current.textContent = String(renderCountRef.current);
  });

  return (
    <p>
      このコンポーネントは
      <b ref={displayAreaRef} />
      回描画されました。
    </p>
  );
};
```

これと同じものをクラスコンポーネントで書くとこんな感じです。`useRef`で得た ref オブジェクトがおおよそコンポーネントの（ステートではない）プロパティに対応していることが分かると思います。それにしても、やっぱりフックを用いた関数コンポーネントのほうがシンプルでいいですね（場合にもよりますが）。

```jsx
export class UseRefSample2 extends React.Component {
  constructor(props) {
    super(props);
    this.displayArea = React.createRef();
    this.renderCount = 0;
  }
  render() {
    return (
      <p>
        このコンポーネントは
        <b ref={this.displayArea} />
        回描画されました。
      </p>
    );
  }
  _effect() {
    this.renderCount++;
    this.displayArea.current.textContent = String(this.renderCount);
  }
  componentDidMount() {
    this._effect();
  }
  componentDidUpdate() {
    this._effect();
  }
}
```

## useLayoutEffect
`useLayoutEffect`は **`useEffect`の亜種**です。基本的な使い方は`useEffect`と同じですが、**コールバック関数が呼ばれるタイミングが違います**。具体的には、`useEffect`はレンダリングの結果が描画された後に呼び出されますが、`useLayoutEffect`はレンダリング結果がDOMに反映された後描画される前に呼び出されます。

レンダリング結果が描画される前にコールバックの処理が走るという特徴のため、**`useLayoutEffect`の処理はレンダリングをブロックし、レンダリング結果がユーザーに見えるのが遅延されます**。ですから、**その必要がない場合は`useEffect`を使うべき**であるとされています。ちなみに、クラスコンポーネントの`componentDidMount`や`componentDidUpdate`のタイミングはこの`useLayoutEffect`と同じです。`useEffect`は、レンダリングをブロックしなくなったという点でこれらの進化形であると言えるでしょう。

`useLayoutEffect`を使う例を挙げてみます。つい先ほどの`useRef`の例を思い出してください。

```jsx:useRefの例（再掲）
export const UseRefSample2 = () => {
  const displayAreaRef = useRef();
  const renderCountRef = useRef(0);

  useEffect(() => {
    renderCountRef.current++;
    displayAreaRef.current.textContent = String(renderCountRef.current);
  });

  return (
    <p>
      このコンポーネントは
      <b ref={displayAreaRef} />
      回描画されました。
    </p>
  );
};
```

これの返り値を見てみると、b要素の中身は最初空です。`useEffect`のコールバックによってこの中身が埋められますが、ここに問題があります。`useEffect`のコールバックはレンダリングが描画後に呼び出されるということは、この中身が空の状態が一瞬ユーザーに見えてしまうのです。実際、下の画像のような描画が表示されてしまいます。

[![Image from Gyazo](https://i.gyazo.com/731e1fbba22fbfd79f3173ddedd1dd86.png)](https://gyazo.com/731e1fbba22fbfd79f3173ddedd1dd86)

`useLayoutEffect`を使用するように書き換えることでこの現象を回避できます。

```jsx:useLayoutEffectの例
import * as React from 'react';
const { useLayoutEffect, useRef } = React;

export const UseLayoutEffectSample = () => {
  const displayAreaRef = useRef();
  const renderCountRef = useRef(0);

  useLayoutEffect(() => {
    renderCountRef.current++;
    displayAreaRef.current.textContent = String(renderCountRef.current);
  });

  return (
    <p>
      このコンポーネントは
      <b ref={displayAreaRef} />
      回描画されました。
    </p>
  );
};
```

## useDebugValue

これが恐らく一番新しいフックで、React Hooksを昔は追っていたという方は知らないかもしれません。その名の通り、これはデバッグに使えるフックです。具体的には、**カスタムフックのデバッグ情報をReactの開発者ツール用拡張機能に表示させることができます**。

これは先ほどの「レンダリング回数を覚えているコンポーネント」をベースにした例です。

```jsx:useDebugValueの例
import * as React from 'react';
const { useEffect, useRef, useDebugValue } = React;

const useRenderCount = () => {
  const renderCountRef = useRef(0);

  useDebugValue(
    `このコンポーネントは${renderCountRef.current}回再描画されました`,
  );

  useEffect(() => {
    renderCountRef.current++;
  });
};
export const UseDebugValueSample = () => {
  useRenderCount();

  return <p>このコンポーネントを開発者ツールで見ると再描画数が表示されます</p>;
};
```

ここで定義されている`useRenderCount`関数はカスタムフックです。カスタムフックについては[筆者の以前の記事](https://qiita.com/uhyo/items/27a83d9044945034c4da)に譲りますが、要するに名前が`use`で始まるただの関数です。普通は上の例のように、いくつかのフック呼び出しをまとめて関数にしたものがカスタムフックです。ただの関数ということは、あるコンポーネントから`useRenderCount`カスタムフックを呼び出すのは、その中身に書かれているフックを全部呼び出すのと同じです。何も特殊なことはありません。

ただ、`useDebugValue`フックは自分がどのカスタムフックから呼び出されたのか検知します[^debugvalue_callstack]。そして、対応するカスタムフックの横に指定したデバッグ情報を表示します。

[^debugvalue_callstack]: ちゃんと調べたわけではありませんが、どうやら`Error`オブジェクトを生成してコールスタックを入手しているようです。

実際、この`UseDebugValueSample`をReact用拡張機能で見ると下の画像のように表示されています。`useDebugValue`フックで指定した値が`useRenderCount`カスタムフックのデバッグ情報として表示されていることが分かります。

[![Image from Gyazo](https://i.gyazo.com/3894c8db5d7c33c69c2321291fa2be16.png)](https://gyazo.com/3894c8db5d7c33c69c2321291fa2be16)

このフックは、これから増えるであろう、カスタムフックをライブラリで提供するような場合に便利かもしれません。

### デバッグ情報の遅延計算

`useDebugValue`の呼び出しはただのJavaScriptコードですから、フックの処理時には普通に実行されます。つまり、開発者ツールを使用していない一般ユーザーがアプリを実行している場合であっても`useDebugValue`の呼び出しは（実際見られることはないので無意味ですが）行われています。

凝ったデバッグ情報を表示したい場合、`useDebugValue`の引数の計算が多少重い処理になるかもしれません。そのような場合、開発者ツールを使わないユーザーの処理が重くなってしまうのは望ましくありません。そのような事態を避けるために、`useDebugValue`の第2引数に、生のデータをデバッグ情報に加工する関数を渡すことができます。こうすると、その関数は実際にデバッグ情報を表示する際に実行されます。これにより、デバッグ情報が必要ない場合に余計な計算を省くことができるというわけです。この機能は以下のように使います。

```jsx
  useDebugValue(
    renderCountRef.current,
    count => `このコンポーネントは${count}回再描画されました`,
  );
```

この例では、第1引数はカウント数という生のデータになり、それを文字列へと加工する処理は遅延されるようになりました。

## useImperativeHandle

これが最後のフックです。このフックは、**コンポーネントのインスタンスが持つメソッドを生成する**ことができるフックです。使い方は例を参照してください。なお、コンポーネントのインスタンスというのは、`ref`で取得できるオブジェクトのことです。`<div ref={myRef} />`のようにDOM要素に対して`ref`を使った場合は生のDOMノードが得られますが、`<MyComponent ref={myRef} />`の場合は`MyComponent`コンポーネントのインスタンス（のようなもの）が得られることになります。`MyComponent`側が`useImperativeHandle`フックを使うことで、ここで得られるインスタンスにメソッドを生やすことができます。

次の例は、少し前に出てきた、現在時刻をリアルタイムに表示するサンプルを改造したものです。

```jsx:useImperativeHandleの例
import * as React from 'react';
const {
  useEffect,
  useRef,
  useCallback,
  useImperativeHandle,
  forwardRef,
} = React;

export const UseImperativeHandleSample = () => {
  const clockRef = useRef();

  // スタートボタンを押したときの処理
  const onStart = useCallback(() => {
    clockRef.current.start();
  }, []);
  // ストップボタンを押したときの処理
  const onStop = useCallback(() => {
    clockRef.current.stop();
  }, []);

  return (
    <div>
      <Clock ref={clockRef} />
      <p>
        <button onClick={onStart}>再開</button>
        <button onClick={onStop}>停止</button>
      </p>
    </div>
  );
};

const Clock = forwardRef((_props, ref) => {
  // 時刻を表示する場所のref
  const displayAreaRef = useRef();
  // リアルタイム表示がオンかどうかのref
  const enabledFlagRef = useRef(true);

  // リアルタイムに時刻を表示する処理
  useEffect(() => {
    let rafid = null;
    const loop = () => {
      // リアルタイム表示がオンのときのみ表示を更新
      if (enabledFlagRef.current) {
        // 現在時刻を表示
        const now = new Date();
        displayAreaRef.current.textContent = `${String(now.getHours()).padStart(
          2,
          '0',
        )}:${String(now.getMinutes()).padStart(2, '0')}:${String(
          now.getSeconds(),
        ).padStart(2, '0')}.${String(now.getMilliseconds()).padStart(3, '0')}`;
      }
      rafid = requestAnimationFrame(loop);
    };
    loop();
    return () => cancelAnimationFrame(rafid);
  });
  // コンポーネントのインスタンスが持つメソッドを宣言
  useImperativeHandle(ref, () => ({
    start() {
      enabledFlagRef.current = true;
    },
    stop() {
      enabledFlagRef.current = false;
    },
  }));

  return <p ref={displayAreaRef} />;
});
```

最後なので例が少し長くなっています。下で定義されている`Clock`コンポーネントが`useImperativeHandle`フックを使用して`start`と`stop`という2つのメソッドを宣言しています。このコンポーネントは`enabledFlagRef`の値を用いて表示を更新するかどうかを制御しており、この2つのメソッドを用いてそれを外部から制御できるようにしようという魂胆です。使う側である`UseImperativeHandleSample`コンポーネントは、`clockRef`に`Clock`コンポーネントのインスタンスを入れて、ボタンが押されるとその`start`や`stop`を呼び出すようになっています。

`useImperativeHandle`を使うときの最初のポイントは、よく見ると`Clock`が`forwardRef`という関数に包まれている点です。`forwardRef`は大雑把に言えば関数型コンポーネントの`ref`を加工したいときに使うものです。今回はまさに`useImperativeHandle`によって`ref`にメソッドを生やそうとしているのでした。
`forwardRef`の引数として関数コンポーネントを渡すのですが、第2引数として`ref`が渡されるようになっています。これがそのまま`useImperativeHandle`の第1引数となります。第2引数はオブジェクトを返す関数です。返されたオブジェクトが持っているメソッドが、そのままこのコンポーネントの`ref`のメソッドとなります。

### 名前について

Imperativeというのは「手続き的」ということです。手続き的というのは、props等によって宣言的にUIを定義するReactの流儀から外れていることを意味しています。実際、この例だって`useImperativeHandle`を使う必然性があるわけではなく、オン/オフのフラグをpropsで渡すことも可能です。そもそもコンポーネントにメソッドを生やすというのはコンポーネントをprops（やコンテキスト）以外の手段で制御しようということですから、真っ向からReactのやり方に反しているのがお分かりになると思います。それでも需要があるからこそこのフックが追加されたのでしょうが、あまり積極的に使うものでもないよというメッセージが名前に表れています。

# まとめ

以上で、React Hooksの最初の正式版が導入されたReact 16.8に存在するフックを全て解説しました。個人的によく使いそうなのは`useState`と`useEffect`、あと`useRef`あたりです。`useRef`は`useEffect`のロジックが複雑化してきたら出番が増えてきます。副作用を複雑化させるのはあまり褒められたことではありませんが。

途中何回かクラスコンポーネントとの比較を挟みましたが、さすが後発のAPIだけあって、よりシンプルで直感的な記述が可能になっているのがお分かりになったことでしょう。そもそも関数コンポーネント自体がシンプルなAPIということもあり、ソースコードのシンプルさに大きく貢献してくれます。また、最初関数コンポーネントで書いていたのに状態が必要になってしまったときに、クラスコンポーネントに書き直す必要がないというのも嬉しい点です[^recompose]。

[^recompose]: `recompose`を使っている人は関数コンポーネントのままでもいけるぞと思ったかもしれません。それはある意味で正しく、React Hooksは`recompose`の進化系と考えることもできます。なお、`recompose`はReact Hooksの登場と同時に機能追加等の停止が宣言されました。今後はReact Hooksが`recompose`に取って代わることになります。

途中何回かリンクしましたが、筆者の他の記事に**カスタムフック**を取り扱ったものがあります。今回紹介したフックたちを組み合わせて**カスタムフック**を作ることこそReact Hooksの本質であると言っても過言ではありません。そう、これを読み終わってなるほどと思ったあなたはまだReact Hooksのスタートラインの3メートルくらい手前にいる状態なのです。ぜひこちらの記事も読んでReact Hooksのスタートを切ってください。

- [作って理解するReact Hooks](https://qiita.com/uhyo/items/27a83d9044945034c4da)