ReduxのReducerでのState操作の基本

# 目次

- [概要](#概要)
- [ReducerでState操作](#reducerでstate操作)
- [オブジェクトのキー補間構文](#オブジェクトのキー補間構文)
- [まとめ](#まとめ)

# 概要
この記事では、Reduxを使用したときのStoreにある**StateをReducerで操作する際の基本**として、主にCRUDアプリケーションをはじめとした非常に多くの場面で利用される**「追加、更新、削除」**についてまとめています。
ReducerでのState操作の方法が分からなくなった時にこの記事を参考にしていただけるようであれば幸いです。
なおこの記事では、Reactを使用していることを前提としています。

### 前提知識

- Reduxの基本的な知識を身につけている(Reduxの基本については[こちらの記事](https://qiita.com/jima-r20/items/4a5d0074e24add0df3fc)にまとめています)
- JavaScript(ES2015以降)で使用できる構文をある程度知っている

### 環境

| 導入ライブラリ   | version  |
|:-------------|---------:|
| React        | 16.13.1  |
| react-redux  | 7.2.0    |
| redux        | 4.0.5    |
| lodash       | 4.17.15  |

# ReducerでState操作
### State操作の掟
Reduxで扱うStateはすべて、Storeというところに格納されています。
そしてそのStoreに格納されている**Stateは直接変更してはいけません**。(イミュータブル)
ではStateを変更したい場合にはどのようにStateを変更すればいいのか以下に操作例をまとめます。

### Stateの操作例
#### 配列のState操作
**悪い例**

```javascript
// Reducer
export default (state=[], action) => {
  switch(action.type){
    // 追加する場合
    case 'ADD':
      return state.push('hi');
    // 更新する場合
    case 'REPLACE':
      return state[0] = 'bye';
    // 削除する場合
    case 'REMOVE':
      return state.pop();
    default:
      return state;
  };
};
```
この場合、エラーとなるかあるいは正しくStateが変更されません。

**正しい例**

```javascript
// Reducer
export default (state=[], action) => {
  switch(action.type){
    // 追加する場合
    case 'ADD':
      return [...state, 'hi'];
    // 更新する場合
    case 'REPLACE':
      return state.map(element => element === 'hi' ? 'bye' : element);
    // 削除する場合
    case 'REMOVE':
      return state.filter(element => element !== 'hi');
    default:
      return state;
  };
};
```
このように書くことで配列のStateを正しく変更することができます。

#### オブジェクトのState操作
**悪い例**

```javascript
// Reducer
export default (state={}, action) => {
  switch(action.type){
    // 追加する場合
    case 'ADD':
      return state.age = '30';
    // 更新する場合
    case 'UPDATE':
      return state.name = 'Sum';
    // 削除する場合
    case 'REMOVE':
      return delete state.name;
    default:
      return state;
  };
};
```
この場合、配列のときと同様にエラーとなるか正しくStateが変更されません。

**正しい例**

```javascript
// Reducer
import _ from 'lodash';

export default (state={}, action) => {
  switch(action.type){
    // 追加する場合
    case 'ADD':
      return {...state, age: 30};
    // 更新する場合
    case 'UPDATE':
      return {...state, name: 'Sum'};
    // 削除する場合
    case 'REMOVE':
      return {...state, age: undefined};
      // または
      return _.omit(state, 'age');  // lodashを使用
    default:
      return state;
  };
};
```
オブジェクト構造のStateの操作はこのように記述します。

配列やオブジェクトの操作で`...state`といったように**スプレッド構文**を使用していることに注目しましょう。これは現在あるStateを変更するのではなく、新たに現在のStateをコピーを生成して、その中で変更を行いそれを新たにStateとしてStoreに格納していることになります。

**ReduxにおけるStateのようにオブジェクトや配列などをイミュータブルに扱いたい場合にスプレッド構文が活躍します。**


# オブジェクトのキー補間構文
一部では**Key interpellation syntax**と呼ばれていました。
これは一体なんなのかというと、コードを見ていただいた方が早いと思うので以下で紹介します。

```javascript
const countryCapital = { japan: 'Tokyo', france: 'Paris' };
const country = 'china';
const capital = 'Beijing';

{...countryCapital, [country]:capital}
// -> { japan: 'Tokyo', france: 'Paris', china: 'Beijing' }
```
ここで注目してもらいたいのが、`{...countryCapital, [country]:capital}`です。`{...countryCapital}`は先程説明したスプレッド構文ですが、その後に続く`[country]:capital`の部分が見慣れない構文なのではないかと思います。この`[country]`は**配列ではありません**。今回の場合ですと、**オブジェクトのキーとして`country`を渡している**ことになります。すなわち`[country]:capital === china:'Beijing'`ということになります。
この構文を知っていれば、ReducerのState操作を便利にしてくれるはずなので、覚えておきましょう。


# まとめ
今回はReduxでのState操作の方法についてまとめました。
操作方法の例を紹介しましたが、前提として最も重要なことは**Stateは直接操作せずイミュータブルに扱う**ということです。
そのための手段として**スプレッド構文やlodashライブラリ、キー補間構文**などがあるということを抑えておきましょう。

# 参考資料
- [Reduxユーザーが最もハマるstateの不正変更とその検出方法](https://blog.mitsuruog.info/2018/02/why-is-immutability-required-by-redux)
- [スプレッド構文 JavaScript|MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
- [JSのスプレッド構文を理解する](https://qiita.com/akisx/items/682a4283c13fe336c547)