【React】Immutable.js から Immer へ切替

## なぜ
ずっと `immutable.js` 使ってますが、`OOP` アプローチに関して、最新何かあるかを探したことろ、`immer` が見つかった。なんと、[React open source award 2020](https://osawards.com/react/) の　`Breakthrough of the year` を受賞されたようです。

## どっちが良い
現時点、どっちもメジャーな `OOP` アプローチです。`immutable.js` は Facebook が管理するライブラリで、結構年数ありました。過去２年のダウンロード数から見ると、最近は `immer` が波に乗ってます。サイズやパフォーマンス的にも `immer` が良さそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160072/6cbed6e3-3d0f-8ddd-8067-6a7543e8a22e.png)


## 個人感想のメリット
`immutable.js` は `redux` 用のライブラリで、`immer` は `React.setState` にも使えます。
`type` の定義が必要なくて、楽になって、コード量は大きく減ってませんが、可読性が向上したと思います。

## 変更点 - Settings
`redux-immutable` と `connected-react-router` の `immutable` ライブラリが必要なくなります。

## 変更点 - Reducer
`reducer` から見ると、`import` のみ変わって、ほとんど修正必要ありません。

```typescript:reducer-immutable.js
import { handleActions, Action } from 'redux-actions';
import { AppState } from '@domains';
import { ActionTypes } from '@constants';
import { App01Payload, App02Payload } from '@actions/app';

const app = handleActions<AppState, any>(
  {
    [ActionTypes.APP_PLUS_REQUEST]: (state: AppState) => state,
    [ActionTypes.APP_PLUS_SUCCESS]: (state: AppState, { payload: { num } }: Action<App01Payload>) => state.plus(num),
    [ActionTypes.APP_PLUS_FAILURE]: (state: AppState) => state,

    [ActionTypes.APP_MINUS_REQUEST]: (state: AppState) => state,
    [ActionTypes.APP_MINUS_SUCCESS]: (state: AppState, { payload: { num } }: Action<App02Payload>) => state.minus(num),
    [ActionTypes.APP_MINUS_FAILURE]: (state: AppState) => state,
  },

  new AppState()
);

export default app;
```

```typescript:reducer-immer
import { handleActions, Action } from 'redux-actions';
import { AppState } from '@domains';
import { ActionTypes } from '@constants';
import { App01Payload, App02Payload } from '@actions/app';

const app = handleActions<AppState, any>(
  {
    [ActionTypes.APP_PLUS_REQUEST]: (state: AppState) => state,
    [ActionTypes.APP_PLUS_SUCCESS]: (state: AppState, { payload: { num } }: Action<App01Payload>) => state.plus(num),
    [ActionTypes.APP_PLUS_FAILURE]: (state: AppState) => state,

    [ActionTypes.APP_MINUS_REQUEST]: (state: AppState) => state,
    [ActionTypes.APP_MINUS_SUCCESS]: (state: AppState, { payload: { num } }: Action<App02Payload>) => state.minus(num),
    [ActionTypes.APP_MINUS_FAILURE]: (state: AppState) => state,
  },

  new AppState()
);

export default app;
```

## 変更点 - ロジック
従来の `typescript type` 定義が省略できるので、少し楽になります。書き方も `immutable.js` 独自の方法じゃなく、`ES6` が書けます。

```typescript:immutable.js
import { Record } from 'immutable';

export interface IApp extends AppProps, Record<AppProps> {
  get<K extends keyof AppProps>(key: K): AppProps[K];
}

export interface AppUIProps {}

export interface AppProps extends AppUIProps {
  // count
  count: number;
}

/**
 * App
 */
export default class AppState extends Record<AppProps>({
  count: 0,
}) {
  plus(num: number) {
    return this.set('count', this.count + num);
  }

  // inner update
  minus(num: number) {
    return this.set('count', this.count - num);
  }
}
```

```typescript:immer
import { immerable, produce } from 'immer';

export default class AppState {
  [immerable] = true;

  count: number = 0;

  plus = (num: number) =>
    produce(this, (draft) => {
      draft.count += num;
    });

  minus = (num: number) =>
    produce(this, (draft) => {
      draft.count -= num;
    });
}

```

## おまけ
`immer` の[公式ドキュメント](https://immerjs.github.io/immer/docs/introduction)は詳しく色んな情報書いています。

`immer` 取り込み済みのプロジェクトは GitHub 公開中です。

https://github.com/wwalpha/project-template-react-redux