以前説明した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を消去できるので、開発初期にはよく使うことになると思います。