react-hooks で redux を書いてみた話

react-hooksでreduxを真剣にいじる機会に恵まれないので、練習がてらボタンをおしたら数字が増えるカウンターを作ってみました
。

# 環境構築

まずは環境構築。絶対に必要なredux関連と型情報をワンセットでインストールしましょう。

```bash
npx create-react-app hooks --template typescript
npm install react-redux 
npm install @types/react-redux
npm install redux
npm install @types/redux
```

生成されたテンプレートのプロジェクトをいじっていきます。
 
## action
まずはactionから定義をします。
今回実装するアクションは以下の通りです。

* カウントが増える `INCREMENT`
* カウントが減る `DECREMENT`



```typescript
export  const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

export function increment() {
    return {
        type: INCREMENT
    }
}

export function decrement() {
    return {
        type: DECREMENT
    }
}

```

## reducer 
次に実際に値を更新したり、編集したりするreducerを実装します。

```typescript
import {INCREMENT, DECREMENT} from "./action";

const initialState = {
    count: 0
}

function reducer(state = initialState, action: {type: string}) {
    switch(action.type) {
        case INCREMENT: 
            return {
                count: state.count + 1
            };
        case DECREMENT: 
            return {
                count: state.count - 1
            };
        default: {
            return {
                count: 0
            }
        }
    }
}

export default reducer;

```

普通であれば、 `combinereducer` で結合しますが、今回はreducerを一つしか作らないので、生のreducerをそのままexportしています。

## store 
値を格納するstoreです。
reducerを受け取ってstoreを生成していますが、このままだとstateの型が不定で後々のつなぎこみで不都合が生じるので、`RootState`を定義しています。

```typescript

import {createStore} from "redux";
import reducer from "./reducer";

export type RootState = ReturnType<typeof reducer>
export const store = createStore(reducer);
```

`ReturnType` は関数の戻り値の型として生成して返します。今回であれば、reducerの戻り値(stateのproperty構造)を型として返してくれます。

つまり、今回のRootStateの中身はこのような感じ

```typescript

type RootState = {
    count:number
}

```

## connect(component)

hooks を使ったものはこの部分が大きく違っています。いままでは、
`mpaStateToProps`
`mapDispatchToProps`
`connect`

を使ってましたが、

`useSelector` : storeからstateを取得
`useDispatch` : storeへ変更を伝えるためのdispatch関数を取得

↑の二つを使うだけでよくなります。

先程、作った `RootState`は `useSelector`に使います。　stateから情報を受け取るのですが、この時stateの情報がないと、型が解決できなくなるので、 `RootState` を作成して型を確定させるのです。

```tsx
import React from "react";
import { useSelector, useDispatch} from "react-redux";
import {RootState} from "./store";
import {increment, decrement} from "./action";

export const Counter: React.FC = () => {
    const dispatcher = useDispatch();
    const state = useSelector((state:RootState) => state);

    return (
        <div className="counter">
            <h1> counter </h1>
            <p>{state.count}</p>
            <button onClick={() => dispatcher(increment())} >count up</button>
            <button onClick={() => dispatcher(decrement())} >count down</button>
        </div>        
    )
}
```


## つなぎ込み

あとは、 Providerにstoreを渡して終了です。

```tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {store} from "./store";
import {Provider} from "react-redux"

ReactDOM.render(<Provider store={store}> <App /> </Provider>, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
```

# 最後に
`mpaStateToProps`
`mapDispatchToProps`
`connect`

という黒魔術を使わなくてよくなり、且つhooksの文法をしっていれば処理の流れを追いやすくなったと思います。
というわけで、時間があれば昔のreactコードのリファクタをしたいと思います。

それでは、よいreactライフを