immutableJSのMapをredux-persistで保存できないときの対処法

以前説明したimmutableJSは簡単にimmutableオブジェクトを扱うことができるのでreduxのreducerで大活躍します。 normalizr風味のreducerを作る場合は、下記のようにimmutableJSのMapとListで簡潔に書く事ができます。

import { ADD_THOUGHT } from "../actions/thought";
import { Map, fromJS } from "immutable";

const initialState = fromJS({
  ids: [],
  entities: {}
});

export default function thought(state = initialState, action) {
  switch (action.type) {
    case ADD_THOUGHT:
      return state
        .setIn(["entities", action.draft.tid], action.draft)
        .set("ids", state.get("ids").push(action.draft.tid));
    default:
      return state;
  }
}

一つ問題があります。immutableJSはfromJSを利用するとMapオブジェクトでstateを保存します。これが、redux-persistを使うとMapで保存できないという問題です。redux-persistはtransformsという機構でserialize, deserializeを行うことができ、ここにモジュールを適用することで保存できるようになります。redux-persistのauthorであるr2tzz氏が提供するredux-persist-immutableがありますが、READMEの情報が少なくよくわかりません。他のソースを読むと、トップレベルでMapの置換を適用するようです。私の今回のreducerではnavというReact Navigatorのreducerがあるので、ここに適用する必要はないと思っていました。

Redux Persist Transform Immutable

Redux Persist Transform Immutableというものもr2tzz氏が提供しています。これは、reducerごとにシリアライズ・デシリアライズをできるものです。これはREADMEもしっかり書いてあります:)

では、実際のコードを見てみましょう。

import immutableTransform from "redux-persist-transform-immutable";

...

  componentWillMount() {
    persistStore(
      this.store,
      {
        storage: AsyncStorage,
        transforms: [
          immutableTransform({
            whitelist: ["user", "draft", "thought"],
            blacklist: ["nav"]
          })
        ]
      },
      async () => {
        await this.store.dispatch(initUser());
        await this._loadAsync();
      }
    ); //.purge();
  }

使い方は、transformsのvalueにimmutableTransformを入れます。whitelist/blacklistで追加します。どちらかを指定すれば片方がいらないかもしれませんが、わかりやすくするためにwhitelist/blacklist両方いれています。React Nativeでの利用なので、storage: AsyncStorageはそのままです。 ちなみにpersistStore(..).perge()でstateを消去できるので、開発初期にはよく使うことになると思います。

Related Posts