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