RFCに追加されたuseEventについて

React RFCs(request for comments)に登場した新しいhooksのuseEventの長所と使い所をまとめてみましたので、参考にして頂ければ幸いです。
## [RFCについて](RFCについて)
Reactのコアメンバーとコミュニティメンバーが共同で新機能を設計できることを目的としており、誰でも参加可能でCLS(貢献者ライセンス契約)受理後にマークダウンファイルPRが提出できるようになります。
PRがマージされるとRFCは「アクティブ」となり、最終的にReactに取り込むことを目標に実装することができます。
詳しくは[こちら](https://github.com/reactjs/rfcs)のREADMEを参照
## [hooksにおける課題（コールバック関数編）](hooksにおける課題（コールバック関数編)
親コンポーネントから子コンポーネントにpropsでコールバック関数を渡すシーンにおいて以下のようなコードがあります。
```js:Chat.tsx
const Chat = () => {
  const [text, setText] = useState('')
  const onClick = () => {
    sendMessage(text)
  }

  return <SendButton onClick={onClick} />
}
```
このコードの問題点はテキストが変更されるたびに、```onClick```関数が再生成されてしまうことで、```SendButton```が毎回再レンダリングされてしまいます。

**useCallbackを使って解決しようとしたパターン**
```js:Chat.tsx
const Chat = () => {
  const [text, setText] = useState('')
  const onClick = useCallback(() => {
    sendMessage(text)
  }, [text])

  return <SendButton onClick={onClick} />
}
```
関数が再生成されない限り再レンダリングが走らないようになりましたが、テキストが変わるたびに```onClick```関数が再生成されて、```SendButton```が毎回再レンダリングされます。

**既存のhooksを使った解決策**

```js:Chat.tsx
const Chat = () => {
  const [text] = useState('')
  const sendRef = useRef(null)
  useLayoutEffect(() => {
    sendRef.current = () => sendMessage(text)
  })

  const onClick = useCallback((...args) => {
    return sendRef.current(...args)
  }, [])

  return <SendButton onClick={onClick} />
}
```
かなり可読性が悪いですよね＾＾；


**useEventで解決するパターン**
```js:Chat.tsx
const Chat = () => {
  const [text, setText] = useState('')
  const onClick = useEvent(() => {
    sendMessage(text)
  })

  return <SendButton onClick={onClick} />
}
```
```useEvent()```を使えば、関数内で使われるプロップやステートが変わっても、同じ参照整合性を維持することができます。

## [hooksにおける課題（useEffect編）](現状のhooksにおける課題（useEffect編）)
次に、ページが更新した際にURLとユーザー名のログを残す実装で```useEffect```を使った例です。
```js:Page.tsx
const Page = () => {
  useEffect(() => {
    logAnalytics('visit_page', route.url, currentUser.name)
  }, [route.url, currentUser.name])
}
```
こちらの問題点は、本来であればURLが変わった時のみ```logAnalytics()```を実行したいのに、ユーザー名が変わったときにも走ってしまいます。`currentUser.name`を消しても、ESLintの```useEffect has amissing dependency```の警告が出てしまいます＾＾；

**useCallbackを使って解決しようとしたパターン**
```js:Page.tsx
const Page = () => {
  const logVisit = useCallback((url) => {
    logAnalytics('visit_page', url, currentUser.name)
  }, [currentUser.name])
  useEffect(() => {
    logVisit(route.url)
  }, [route.url])
}
```
これでも、```currentUser.name```が変わるたびに関数が再生成される問題は残っています。

**useEventで解決するパターン**

```js:Page.tsx
const Page = () => {
  const logVisit = useEvent((pageUrl) => {
    logAnalytics('visit_page', pageUrl, currentUser.name)
  })
  useEffect(() => {
    logVisit(route.url)
  }, [route.url])
}
```

これで、```useEffect```関数本体から```currentUser.name```を削除し、```route.url```が変更されたときのみ```logVisit()```が再生成されずに実行することができるようになります。依存配列もなくなりスッキリしますね！

## 考察
useEventは現状のhooksにおける大きな課題になっている、コールバック関数が再生成されることによる再レンダリングとuseEffectやuseCallback内での無駄な実行を防止し、依存配列の指定に早く公式採用されるのを心待ちにしております。

## 参考
- [Introducing the React RFC Process](https://reactjs.org/blog/2017/12/07/introducing-the-react-rfc-process.html)
- [useEvent GitHub](https://github.com/reactjs/rfcs/pull/220)
- [useEventの参考になった海外記事](https://javascript.plainenglish.io/react-useevent-the-latest-and-greatest-react-hook-56ae71febbf3)