reactでuseStateとuseCallbackを使う

# 背景

reactでこんなことを実現したいとします。

![b93ad4f3459da18cfff396179fbbeb83.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172187/363c9687-cd08-f2d3-3f05-095adb14293e.gif)

まあよくある、ボタンを押すと数字がインクリメントされるやつです。
日本ではvueが盛り上がっていますが、reactも盛り上がって欲しいところ...！
あまり日本語の文献がないので書いていきます。

react16.8より追加されたHookを使って作っていきましょう。
typescriptで書いていきます。

## だめな例1

```tsx
// Sample.tsx
import React, { useState } from "react";

const Sample = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        click here!
      </button>
      <p>count: {count}</p>
    </div>
  );
};
export default Sample;
```

これをすると、tslintから以下のような警告が出てしまいます。
![スクリーンショット 2019-07-26 0.35.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172187/57be89ef-b33a-f125-2f06-80d83bdd1044.png)

`Lambdas are forbidden in JSX attributes due to their rendering performance impact (jsx-no-lambda)`
レンダリングのときのパフォーマンスがよろしくないから、jsx(拡張子は.tsxですが)のなかでlambdaを書かないでね、と言っています。
このように書くと、レンダリングのたびにこの関数が新しく宣言されてしまうので、無駄が発生してしまいます。

## だめな例2
すこし書き換えてみましょう。

```tsx
import React, { useState } from "react";

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

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <button onClick={handleClick}>click here!</button>
      <p>count: {count}</p>
    </div>
  );
};
export default Sample;
```

`onClick`の処理の部分を、`handleClick`として切り出してみました。
これでtslintからは怒られなくなります。
ですが、これでもやはり`handleClick`がレンダリングのたびに宣言されてしまいます。
[この記事](https://medium.com/@jan.hesters/usecallback-vs-usememo-c23ad1dc60)や、[このstackoverflowの回答](https://stackoverflow.com/a/46138370/11450994)に書いてあります。

## いいけど面倒な例→だめでした

なるほど、では更に書き換えてみましょう。

```tsx
import React, { useState } from "react";

const handleClick = (
  count: number,
  setCount: React.Dispatch<React.SetStateAction<number>>
) => () => {
  setCount(count + 1);
};

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

  return (
    <div>
      <button onClick={handleClick(count, setCount)}>click here!</button>
      <p>count: {count}</p>
    </div>
  );
};
export default Sample;
```

こんどは`handleClick`の関数宣言を、Sampleの外に出してみました。
~~これでレンダリングのたびに宣言される問題は解決します。~~ →解決しないらしいです...

ですがなんでしょうか、このoverkill感。
typescriptというのも相まって、単にインクリメントさせたいだけなのに記述量が半端ない...泣

ちなみに筆者はずっとこれで書いていました、上記でも紹介した[この記事](https://medium.com/@jan.hesters/usecallback-vs-usememo-c23ad1dc60)に出会うまでは。

## useCallbackを一緒に使う→まだだめ

外に出すと宣言が面倒です、できれば関数の中に書いてスコープの恩恵を受けつつ、レンダリング時に再定義しないようにしたい。。。
そんな事できないと思ってました。
全然できました。
[useCallback](https://reactjs.org/docs/hooks-reference.html#usecallback)というhookを使いましょう。

```tsx
import React, { useState, useCallback } from "react";

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

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <button onClick={handleClick}>click here!</button>
      <p>count: {count}</p>
    </div>
  );
};
export default Sample;
```

ただ、このままだとcountが更新されるたびに定義されてしまいます

## useCallbackを一緒に使う→OKバージョン
```tsx
import React, { useState, useCallback } from "react";

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

  const handleClick = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  return (
    <div>
      <button onClick={handleClick}>click here!</button>
      <p>count: {count}</p>
    </div>
  );
};
export default Sample;
```




最高ですね、めちゃくちゃスッキリ。
もっと早く知っていたかった...独学のつらみです。

ちなみに、 `useCallback` と似たようなものに `useMemo` があります。
これらの違いに関しては[この記事](https://qiita.com/seya/items/8291f53576097fc1c52a)がわかりやすいです。

だれかの参考になれば幸いです。