いまReduxを導入するならRedux Toolkitを使うべき

最近 Redux Toolkit というReduxのツールがあることを知りました。
公式ドキュメントのチュートリアルを写経してみたところ、かなり良かったのでご紹介します。

## Redux Toolkit とは

[Redux Toolkit](https://redux-toolkit.js.org/)とはReduxをより簡潔に記述するためのツールです。

はじめは`redux-starter-kit`という名前で2018年から開発され、2019/10にv1.0がリリースされました。

Redux Toolkitは公式に作られたツールでReduxの公式サイトでも[紹介するページ](https://redux.js.org/redux-toolkit/overview)があります。

## サンプルコード

Redux Toolkitを使ったコードを見てみましょう。

```js:todosSlice
const todosSlice = createSlice({
  name: 'todos',     // ①
  initialState: [],  // ②
  reducers: {
    addTodo(state, action) {  // ③
      const { id, text } = action.payload
      state.push({ id, text, completed: false }) // ④
    }
  }
})

// ①：Sliceの名前。Action Typeのプレフィックスに使われる
// ②：Stateの初期値。Todoの配列を持つ。
// ③：Reducerの定義。Action Typeが`todos/addTodo`のAction Creatorが自動的に生成される
// ④：stateの配列にTodoのデータを追加する。
```

上記のコードは[チュートリアル](https://redux-toolkit.js.org/tutorials/intermediate-tutorial)から抜粋したもので、Todoリストのデータを持つSlice[^slice]です。

Redux ToolkitではReducerを定義するとAction Creatorを自動的に生成してくれます。
Action Typeを自分で管理する必要もありません。

上記の例では`reducers`に対応するObjectに`addTodo`メソッド(③)があります。
この場合、`addTodo()`というAction Creatorが生成されます。
`dispath(addTodo(payload))`のように呼び出すと、`addTodo`メソッド(③)の内部の処理が行われます。

[^slice]: Redux Toolkitでは`Slice`という表現が出てくるのですが、Reducer/Action/Stateをひとかたまりにした概念と自分は理解しています。

ちなみにReducerとAction Creatorをそれぞれ定義して書くこともできます。

```js:ReducerとActionCreatorをそれぞれ定義した例
...
reducers: {
  addTodo: {
    reducer(state, action) {
      const { id, text } = action.payload
      state.push({ id, text, completed: false })
    },
    prepare(text, id) {
      return { payload: { text, id } }
    }
  }
}
```

## Redux Toolkitを使うメリット

### コード量が少なくなる

Redux Toolkitを使うとコード量を減らすことができるのが一番のメリットです。

サンプルコードをRedux Toolkitを使わずに書くと下のようになります。

```js:ReduxToolkitを使わずに書いた例
// Action
const ADD_TODO = 'ADD_TODO'

const addTodo = (text, id) => ({
  type: ADD_TODO,
  id,
  text
})

// reducer
const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ]
    default:
      return state
  }
}
```

前節のサンプルコードと比較すると、Actionのコードが必要なので長くなってます。
また、reducerの部分もRedux Toolkitを使ったものと比べて冗長な感じがします。

### コードの可読性が高くなる

Redux ToolkitではActionとReducerが同じオブジェクト内で定義されます。
その結果、凝集度が高く可読性コードになっています。

Redux Toolkitを使わない場合、ActionとReducerのコードが違いファイルに定義されたり、同じファイル内でも離れた位置に定義されるのでコードが見づらくなります。

### stateのイミュータブル性を意識しなくてよい

Redux Toolkitを使った場合、stateのイミュータブル性を気にすることなくコーディングができます。
前節のサンプルコードのreducersを見ると、pushメソッドを使ってstateに変更を加えています。

```js
...
reducers: {
  addTodo(state, action) {
    const { id, text } = action.payload
    state.push({ id, text, completed: false }) // stateに変更を加えている
  }
}
...
```

Reduxのreducerでは必ずイミュータブルなstateを返す必要がありました。
もし下記の例のようにstateを直接変更して返した場合、正しく再レンダリングされないことがあります。

```js
const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      state.push({ id, text, completed: false }) 
      return state // stateを返すと再レンダリングされない
    default:
      return state
  }
}
```

この仕様はバグの温床になることがありますが、Redux Toolkitを使えば安心です。

### TypeScriptの型制御が効く

TypeScriptでReduxを使う場合、[typesafe-actions](https://github.com/piotrwitek/typesafe-actions) や [typescript-fsa](https://github.com/aikoven/typescript-fsa) などのツールを使っている人が多いかと思います。

Redux Toolkitを使えばそれらのツールを使わなくても型制御を効かせてReduxのコーディングができます。

[Redux Toolkitのチュートリアル](https://redux-toolkit.js.org/tutorials/intermediate-tutorial)のコードをTypeScriptで書き直したものを[こちら](https://codesandbox.io/s/rtk-ts-convert-todos-example-d0nk7)のCode Sandboxに作成したので触ってみてください。
特に`src/features/todoSlice.ts`を触るとstateにも型制御が効いていることがわかると思います。

## 最後に

Redux ToolkitはReduxを簡潔に書くことを可能にして、なおかつTypeScript対応など安全にReduxを扱うことができるようになります。

[Reduxの公式サイト](https://redux.js.org/redux-toolkit/overview)でも`we strongly recommend that you use it.`と書いてありますし、いまReduxを導入するならRedux Toolkitを入れない理由はないと思います。

ぜひお試しを！

### 参考

本記事で参考にしたTodoListのソースコード

- Reduxの例：https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/todos
- Redux Toolkitの例：https://codesandbox.io/s/rtk-convert-todos-example-uqqy3?from-embed=&file=/src/features/todos/todosSlice.js
- TypeScript + Redux Toolkit の例：https://codesandbox.io/s/rtk-ts-convert-todos-example-d0nk7