React Hooks + Redux Hooks with Redux toolkit + Type Script入門 on Apr, 2020

ReactのHooksの登場で、関数コンポーネントは使いやすくなりましたね。
2019年6月には、ReduxにもHooksが登場したことで、さらに柔軟性が増しました。

## create-react-app
TypeScriptでReactプロジェクトを作る場合、`--typescript`オプションから`--template`オプションを使うよう推奨になりました。

```
$ create-react-app my-app --template typescript
```
その他もろもろも入れます。

```
$ npm install --save typescript @types/nodes
$ npm install --save react-router react-router-dom @types/react-router-dom
$ npm install --save react-redux @types/react-redux
$ npm install --save @types/react
```

`@types/~` パッケージは、NPMパッケージにTypeScriptの型定義が含まれていない場合に、あとから型定義情報だけを追加するための型パッケージです。

## 基本形: useState
Hooksは、今まで状態(state)を持つことができなかった関数コンポーネントに、状態をもたらすことを可能にしました。
関数でありながら、Class Componentのように状態を持つことが出来、状態変化が発生すれば再renderされます。

**参考**
[React HooksのuseStateがどういう原理で実現されてるのかさっぱりわからなかったので調べてみた](https://sbfl.net/blog/2019/02/09/react-hooks-usestate/)

基本形は、下記のようになります。

```JSX
interface MyProps {
    initialCount: number;
}
function Counter(props: MyProps) {
  const [count, setCount] = useState(props.initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(props.initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}
```

 * count
    * 状態を保持する変数です。constなので再代入できません。
 * setCount()
    * countを更新する関数です。このコンポーネントでは、onClickを押されるたびにsetCountによってcountの数字をいじっています。

`setCount()` が発火するたびに、コンポーネントは再レンダリングされます。

Hookの利用には、いくつかのルールがあり（例えばIf文でHooksの順序を変えてはいけないetc）、一度目を通すとよいと思います。

[フックのルール](https://ja.reactjs.org/docs/hooks-rules.html)

## 発展: useReducer
`useState` では初期値のセット、値の更新しかできず、複雑な処理はコンポーネント内のいずれかに記述することになります。
`useReducer` では、useStateより強力な状態管理を提供してくれます。

```JSX
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
```

 * state
    * `useState()` のときと同様、状態を保持する変数です。
 * dispatch()
    * アクションをディスパッチする関数です。


Redux同様、 `dispatch()` によってアクションをディスパッチします。
reducerは、アクションに応じて、新たなStateを返す関数です。
つまり、アクションに応じて、どのような処理をするかは、コンポーネントの中からリデューサーに移ることになります。

とはいえ、Reduxと異なり、useReducerは、あくまでそのコンポーネント単位での状態に過ぎません。
Reduxのように状態をStoreで一元管理したい場合は、Redux Hooksを導入します。

## 応用: Redux Hooks
Redux側から提供されるカスタムフックです。

従来の`connect()`の代わりに、dispatchとstateのどの部分をComponentにリンクさせるかを、Hooksで定義することが出来ます。

```JSX
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { incrementAction, decrementAction } from "./Actions";

const counterSelector = state => state.counter;

const Counter = props => {
  const dispatch = useDispatch();
  const counter = useSelector(counterSelector);

  return (
    <>
      <p> count: {counter} </p>
      <button onClick={dispatch(incrementAction())}>increment</button>
      <button onClick={dispatch(decrementAction())}>decrement</button>
    </>
  );
};
```

* dispatch()
    * `useReducer` 同様、アクションをディスパッチする関数です。
* useSelector()
    * storeにあるstateの中から、どの部分を利用するかを選択するセレクタです。

これらにより、従来のContainerに相当するコンポーネントを簡単に書くことが出来ます。
今までは、 `mapStateToProps()` や `mapDispatchToProps()` を利用し、stateとdispatchをコンポーネントのProps経由で割り当てていましたが、それがコンポーネント内に記述できるため、かなりすっきりします。

### Redux hooksの注意点
[Usage Warnings](https://react-redux.js.org/api/hooks#usage-warnings)に、利用にあたってのいくつかの注意点が記載されています。
その１つが **Zombie Children** 問題です。これは、Propsが最新である保証がない、という問題です。

詳細はリンク先を見ていただくとして、基本的に、

* useSelector内で、propsを利用したstateのセレクトはしない
* セレクタで抽出するデータが存在しない可能性がある場合には、その値を検証して処理を行うようにする
    * e.g. 
だめな例 `state.todos[props.id].name` （先に`state.todos[props.id]`を検証してからnameプロパティを読み出すべき）

といったことに留意する必要があります。

## 更に楽をする: Redux Toolkit
Redux Hooksの導入により、ボイラープレートの記述は大きく減ることになりますが、それでもreducerやactionの定義は煩雑です。
Ducksパターンを容易に導入できる `@reduxjs/toolkit` を導入します。
Ducksパターンは、Reduxの記述パターンの１つで、actionType、action、reducerをなるべくまとめるパターンです。

```
$ npm install --save @reduxjs/toolkit
```

今回は、createSliceを導入します。

```JSX
import { createSlice, PayloadAction } from "@reduxjs/toolkit";

export class User {
  name?: string;
  age?: number;
}

const userModule = createSlice({
    name: "user",
    initialState: 0,
    reducers: {
        update: (state, action: PayloadAction<User>) => {
            return {
                ...state,
                action.payload
            }
        incrementAge: (state) => {
            return {
                ...state,
                age: state.age + 1
            }
        }
    }
});
```

* createSlice()
    * リデューサーとアクションがひとまとめになったモジュールを作ってくれるファクトリー
    * 今回でいうと、`counterModule.reducer`のなかにリデューサーが、`counterModule.actions`のなかにアクション（正確にはアクションクリエイター）が入っています。
* User class
    * 外部でも利用できるよう、予めこのSliceで取り扱うStateを定義します。InterfaceやTypeでも良いです
* PayloadAction
    * createSlice()にわたす引数内で、reducersにrecuderを記述します。その際、各アクションのペイロードに何を渡すべきかの型情報をPayloadActionとジェネリクスを利用して定義してあげると楽です。
    * ここではUserを定義していますが、何でもOK

この`createSlice()`で作成されたリデューサーは、`combineReducers()` によってまとめることが可能です。

```JSX
const rootReducer = combineReducers({
    user: userModule.reducer
});
```


そのため、Storeへの登録も従来どおり

```JSX
const store = configureStore({
        reducer: rootReducer,
    });
```

といった形で行なえます。

### 非同期の定義
```TypeScript
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { Dispatch } from "react";

const userModule = createSlice({
  // ここにsliceの中身の定義。ここには副作用のない処理だけ。
})

export function getUserAsync() {
  return async function(dispatch: Dispatch<any>) {
    try {
      const response = await hogeService.getUser();
      dispatch(update(response.data.user));
    } catch(err) {
      // エラー処理
    }
  }
}
```

* 非同期処理は、createSlice内で定義せず、別関数として定義してexportします
* 関数では、(dispatch: Dispatch) => Promise な関数を返却するようにします。
* async function内に処理を記載します。
* Dispatch型情報ではジェネリクスとしてActionにわたすべき型を定義可能です（今回はanyとしています）。
    * これはcreateSlice側でPayloadActionを定義しておけば、Component側で呼び出す際に補完されます。

Componentで利用する場合には、下記のようになります。

```JSX
import { useSelector, useDispatch } from "react-redux";
import { RootState } from '../stores';
import { User, update, getUserAsync  } from '../stores/counter';


export default function UserPage() {
  const user = useSelector<RootState, User>(state => state.user);
  const dispatch = useDispatch();

  dispatch(getUserAsync());

  return (
    <div>
      <p>user.name</p>
      <p>user.age</p>
    </div>
  )
}
```

TypeScriptの型補完は、他にも良い書き方がありそうなのですが、今のところ私はこうしてます。

詳しくは、公式にありますので、ここを見て自分なりにやると良さそうです。
**Usage With TypeScript | Redux Toolkit**
https://redux-toolkit.js.org/usage/usage-with-typescript

## まとめ
* `useState`
    * 関数コンポーネントに固有の状態をつくれる
* `useReducer`
    * より複雑な状態処理をディスパッチすることで処理できるようになる
* Redux Hooks
    * Reduxと統合することが出来る