React + useLatest: 非同期の処理が実行されたとき最新の状態を得る

非同期の処理が実行されたとき最新の状態を得るためのフック`useLatest`のご紹介です。

# 関数がつくられたときの状態を保つのがReactの原則
Reactの「コンポーネント内に書かれた関数からは、その関数が作成された時の`props`や `state`が『見え』」ます(「[関数内で古い props や state が見えているのはなぜですか？](https://ja.reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function)」原文:「[Why am I seeing stale props or state inside my function?](https://reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function)」)。外から勝手に書き替えられることによるバグが防げるので安心です。

もっとも、非同期の処理では、関数が実行されたときの最新の状態を使いたいことも少なくありません。React公式サイトの「[フックに関するよくある質問](https://ja.reactjs.org/docs/hooks-faq.htm)」には、つぎのような[コード例](https://ja.reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function)が示されています。

#### コード001■関数はつくられたときの状態変数の値を保つ
```react
import React from 'react';

function Example() {
  const [count, setCount] = useState(0);

  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={handleAlertClick}>
        Show alert
      </button>
    </div>
  );
}
```

ページの[Click me]ボタンを押すと、その数だけ数値が加算されます。[Show alert]はその数値を3秒後に警告ダイアログで表示するボタンです。ただし、その値はボタンを押したときのまま保たれます(図001)。あとにカウントアップした値は見えないのです。以下のCodePenのサンプルで、動きは確かめられます。

#### 図001■警告ダイアログにはボタンを押したときの値が示される
![2008003_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128949/28cfc387-466f-debe-d576-8a1d456c27c7.png)

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="js,result" data-user="FumioNonaka" data-slug-hash="rNejyoj" 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: Seeing stale props">
  <span>See the Pen <a href="https://codepen.io/FumioNonaka/pen/rNejyoj">
  React: Seeing stale props</a> by Fumio Nonaka (<a href="https://codepen.io/FumioNonaka">@FumioNonaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

これが意図した結果であれば構いません。けれど、最新の値を使って処理したい場合もあるはずです。


# useLatestフックで最新の状態を得る
それでは、非同期の処理で最新の状態を得るにはどうしたらよいでしょう。前出「[関数内で古い props や state が見えているのはなぜですか？](https://ja.reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function)」はつぎのように説明します。

> 非同期的に実行されるコールバック内で、意図的に`state`の最新の値を読み出したいという場合は、その値を`ref`内に保持して、それを書き換えたり読み出したりすることができます。

何を言っているのかよくわからない、あるいは手間がかかりそう、と思った人は、[`react-use`](https://github.com/streamich/react-use#--------------------react-use------------------)の[`useLatest`](https://github.com/streamich/react-use/blob/master/docs/useLatest.md#uselatest)を使うとよいでしょう。インストールはnpmで行います。

```terminal
npm i react-use
```

フック[`useLatest`](https://github.com/streamich/react-use/blob/master/docs/useLatest.md#reference)の戻り値は、[`useRef`](https://ja.reactjs.org/docs/hooks-reference.html#useref)と同じ`ref`オブジェクトです。ただし、引数には最新値がほしい状態変数を渡します。`ref`オブジェクトですから`current`プロパティをもち、その中に収められているのが最新の値です。

前掲Reactサイトのコード例に組み込めば、つぎのようになります。モジュールで用いるときは、`useLatest`をあらかじめ`import`してください。これで警告ダイアログは、コールバックが呼び出されたときの最新の状態変数値(`count`)を示します。

#### コード001■useLatestフックで最新の状態変数の値を得る
```react
import React from 'react';
import { useLatest } from 'react-use';

const Example = () => {
  const [count, setCount] = React.useState(0);
  const latestCount = useLatest(count);

  function handleAlertClick() {
    setTimeout(() => {
      alert(`Latest count value: ${latestCount.current}`);
    }, 3000);
  }

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={handleAlertClick}>
        Show alert
      </button>
    </div>
  );
};
```

モジュール化したサンプルを[CodeSandbox](https://codesandbox.io/s/react-uselatest-hook-ug7fy)に掲げました。`useLatest`を用いたコードは、[`src/App.js`](https://codesandbox.io/s/react-uselatest-hook-ug7fy?file=/src/App.js)でお確かめください。


# `useLatest`の実装は10行足らず
おまけとして、フック`useLatest`の実装を確かめておきましょう。TypeScriptファイルの[ソースコード](https://github.com/streamich/react-use/blob/master/src/useLatest.ts)を見ると、なんと10行にも及びません。そして、`useRef`フックが用いられています。前述のReactのドキュメントが説明していた「意図的に`state`の最新の値を読み出したいという場合は、その値を`ref`内に保持して」というのは、こういうことだったのです。