LoginSignup
2
2

More than 3 years have passed since last update.

redux-persistで永続化したストアをmigrationする

Last updated at Posted at 2020-12-28

やりたいこと

redux-persistで永続化ストアの構造を変えたときに、古い構造のストアがlocalstorageから読み込まれてしまう。
ライブラリに用意されているcreateMigrateという関数を使えば永続化ストアの移行ができるので、やり方を記載する。

結論

  1. persistConfigにversion, migrateを追加しろ
  2. createMigrateに移行後のバージョンのストア定義を渡せ
rootRedux.ts
const migrations = {
  1: (state: any) => {
    return {
      ...state,
      post: {
        migrateTest: 'uno',
      },
    };
  },
}

const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['post'],
  version: 1,
  migrate: createMigrate(migrations, { debug: true }),
};
const reducer = persistReducer(persistConfig, rootReducer);

以下詳細を記載します。

前提

postというストアだけをlocalstorageに永続化している。

rootRedux.ts
// 永続化設定部分のみ抜粋
const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['post'],
};
const reducer = persistReducer(persistConfig, rootReducer);

postストアの構造はこういった形。

postRedux.ts
// 構造定義のみ抜粋
const initialState: InitialState = {
  title: '',
  body: '',
};

構造を変えてみる

migrateTestというキーを追加してみる。
この状態で変更を反映しても、redux-persistはlocalstorageからストアを復元するため、migrateTestが追加されない。

postRedux.ts
const initialState: InitialState = {
  title: '',
  body: '',
  migrateTest: '0', // 追加
};

migrationする

さて本題だが、redux-persistが用意してくれているcreateMigrate関数を使ってマイグレーションを行う。

configのversionを上げる

まずはpersistReducerの第一引数に渡すコンフィグに、versionというキーでnumberを渡す。

redux-persistはlocalstorageのversionと、configのversionを見比べて、バージョンが上がっていたらマイグレーションを実行する。
ちなみにconfigにバージョンを指定しないと、「-1」がlocalstorageに記録される。

rootReducer.ts
const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['post'],
  version: 1, // 追加
};

migrationManifestを指定する

続いて、コンフィグにmigrateというキーでcreateMigrateの返り値を渡す。
createMigrateは第一引数にmigrationManifest、第二引数に{debug: boolean}型のオブジェクトを取る。
{debug: true}にすると、ならコンソールにマイグレーションのログが表示されるようになる。

ちなみに、{debug: true}であってもNODE_ENVproductionならログは表示されない。

rootReducer.ts

const migrations = {
  // 後述
}
const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['post'],
  version: 1,
  migrate: createMigrate(migrations, { debug: true }),
};

migrationManifestはstring型のキーと、stateを取ってstateを返す関数のバリューを持つオブジェクト。

redux-persist/types/types.d.ts
interface MigrationManifest {
  [key: string]: (state: PersistedState) => PersistedState;
}

そして、migrationManifestを記述する。
キーはマイグレーション後のバージョン。引数のstateにはlocalStorageから復元したstateが入る。
スプレッド構文で一旦展開し、上書きしたいストアを定義し直す。

rootRedux.ts
const migrations = {
  1: (state: any) => {
    return {
      ...state,
      post: {
        migrateTest: 'uno',
      },
    };
  },
}

まとめると、以下のようなコードになる。

rootRedux.ts
const migrations = {
  1: (state: any) => {
    return {
      ...state,
      post: {
        migrateTest: 'uno',
      },
    };
  },
}

const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['post'],
  version: 1,
  migrate: createMigrate(migrations, { debug: true }),
};

ちなみにmigrationsには過去のバージョンを残さなくても問題ない。
上記の例で言えば、バージョン2に上げたくなったら1のオブジェクトを消しても構わない。

const migrations = {
-  1: (state: any) => {
-    return {
-      ...state,
-      post: {
-        migrateTest: 'uno',
-      },
-    };
-  },
+  2: (state: any) => {
+    return {
+      ...state,
+      post: {
+        migrateText: 'dos',
+      },
+    };
+  }
}

複数のバージョンをmigrationManifestに記載すると順にstateを書き換えていくため、場合によっては過去のバージョンを残すやり方もある。
(個人的には最新バージョンだけ定義する書き方のほうがバグが少なそうに思えます。)

1: (state: any) => {
  return {
    ...state,
    post: {
      migrateTest: 'uno',
    },
  };
},
2: (state: any) => {
  return {
    ...state,
    post: {
      title: 'タイトル',
    },
  };
}

=> state:
{
  title: 'タイトル',
  migrateText: 'uno',
}

Appendix

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2