reduxをcustom hooksと使って関心の分離（SoC）とロジックの再利用をしたい

## はじめに
- [こちら](https://qiita.com/daishi/items/2fa5429122a2a2957b9e)の記事を大いに参考にしてます。
- ある程度Reactとreduxに関しての知識がある読者を想定しております。
- Reactにおいての関心の分離については賛否両論ありますのであくまで一案であるということで。
- 筆者はredux歴がたったの二週間（執筆時点）なので頭おかしいこと書いてる可能性が大いにあります。温かい目で見て、よろしければコメントでご指摘ください。

## やりたいこと
- reduxと密に関係するロジックのほとんどをcustom hookとして別ファイルに置いといて他コンポーネントでも使えるようにしたい。
- コンポーネント内でdispatchの文字を何度も見たくない。dispatchを呼び出すのはcustom hooks内からだけにしたい。

## 開発環境
- TypeScript(3.7.2)
- react(16.12.0)
- redux(4.0.5)
- react-redux(7.2.0)

## 使わないもの
### Middleware
Middlewareの詳しい説明は省きますが（なにせ自分自身がよく理解できてないので）簡単に言うとreduxを使う上で非同期処理の扱いを助けてくれるやつです。そんな便利そうなものを使わずにどう非同期処理を扱うのかというと、__custom hookに丸投げします。__

### Action creator関数
TypeScriptの型定義で事足りると思い使わないことにしました。
Action creatorの中でやりたい処理に関しては、__custom hooksに丸投げします（二回目）。__

###Redux ToolKit
便利そうですがまだ学習してないので今回はスルー。

## 丸投げってなんやねん
ごもっともです。これからその丸投げの中身を、おなじみカウンターの例を用いて解説していきたいと思います。

## storeの設定
```JSX
import { createStore } from 'redux'

type State = {
  count: number
}

type Increment = {
  type: 'INCREMENT'
}

type Decrement = {
  type: 'DECREMENT'
}

type CounterActions = Increment | Decrement

const initialState:State = {
  count: 0,
}

const reducer = (state = initialState, action: CounterActions) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 }

    case 'DECREMENT':
      return { ...state, count: state.count - 1 }

    default:
      return state
  }
}

export default createStore(reducer)
```
`action`の型を`CounterActions`に定義した事によって、`'INCREMENT'`か`'DECREMENT'`以外の値を`type`プロパティに使おうとした場合エラーを吐いてくれます。

##custom hooks
###useCounter
```jsx
import { useDispatch } from 'react-redux'
import { Dispatch } from 'react'

import { CounterActions } from '../redux/action'

const useCounter = () => {
  const dispatch = useDispatch<Dispatch<CounterActions>>()
  return {
    increment: () => dispatch({ type: 'INCREMENT' }),
    decrement: () => dispatch({ type: 'DECREMENT' }),
  }
}

export default useCounter
```
`dispatch`はcustom hook内で呼び出しているのでコンポーネント側からはただ`increment()`と呼ぶだけです。

__普段action creator関数内で行う処理もこれらの関数の中で行います。__

機能追加もここの関数をいじるだけで簡単にできてうれしい。
###useAsyncCounter
```jsx
import { useDispatch } from 'react-redux'
import { Dispatch } from 'react'

import { CounterActions } from '../redux/action'

const useAsyncCounter = () => {
  const dispatch = useDispatch<Dispatch<CounterActions>>()
  return {
    incrementAsync: () => {
      setTimeout(() => dispatch({ type: 'INCREMENT' }), 3000)
    },
    decrementAsync: () => {
      setTimeout(() => dispatch({ type: 'DECREMENT' }), 3000)
    },
  }
}

export default useAsyncCounter
```
__非同期処理もこれらの関数内で行います。__

##コンポーネント
###App
```jsx
import React from 'react'
import { Provider } from 'react-redux'

import store from './store'
import CounterDisplay from './components/CounterDisplay'
import CounterButtons from './components/CounterButtons'

const App = () => {
  return (
    <Provider store={store}>
      <div id='app'>
        <CounterDisplay />
        <CounterButtons />
      </div>
    </Provider>
  )
}
export default App
```
###CounterDisplay
```jsx
import React from 'react'
import { useSelector } from 'react-redux'

import { State } from '../redux/action'

const CounterDisplay = () => {
  const count = useSelector((state: State) => state.count)
  return <div id='display'>{count}</div>
}

export default CounterDisplay
```
###CounterButtons
```jsx
import React from 'react'

import useCounter from '../hooks/useCounter'
import useAsyncCounter from '../hooks/useAsyncCounter'

const CounterButtons = () => {
  const { increment, decrement } = useCounter()
  const { incrementAsync, decrementAsync } = useAsyncCounter()

  return (
    <div id='buttons'>
      <button className='increment' onClick={() => increment()}>
        +
      </button>
      <button className='decrement' onClick={() => decrement()}>
        -
      </button>
      <button className='increment' onClick={() => incrementAsync()}>
        async +
      </button>
      <button className='decrement' onClick={() => decrementAsync()}>
        async -
      </button>
    </div>
  )
}

export default CounterButtons
```
onClickの中身がスッキリしてて、Foo↑気持ちぃ～
##おわりに

この手法を使ってて気になったのが、custom hookから返された関数をuseEffect内で使った際に意図しない形で何度も呼ばれてしまったりすることですが、これらはcustom hook内で返す関数をuseCallbackに包んであげればほぼ解決します。

最初はconnectやthunkを使って書いていたのですが、ボイラープレートの多さと、HOCによるコンポーネントツリーの汚染、コンポーネントファイルにいらんコードが増えるのにモヤッとしたので代替案を探してみました。

Redux ToolKitもこれらの問題を解決するのに役立ちそうなのでそろそろ手を出してみようとおもいます。