更新時にredux状態ツリーを永続化するにはどうすればよいですか?


92

Reduxドキュメントの最初の原則は次のとおりです。

アプリケーション全体の状態は、単一のストア内のオブジェクトツリーに保存されます。

そして、私は実際に私がすべての校長をよく理解していると思いました。しかし、私は今、アプリケーションが何を意味するのか混乱しています。

アプリケーションがウェブサイトの少し複雑な部分の1つにすぎず、1ページで機能する場合、私は理解しています。しかし、アプリケーションがWebサイト全体を意味する場合はどうでしょうか。状態ツリーを維持するためにLocalStorageやCookieなどを使用する必要がありますか?しかし、ブラウザがLocalStorageをサポートしていない場合はどうなりますか?

開発者がどのように状態ツリーを保持しているか知りたいです!:)


2
それは一種の広い質問です。あなたはあなたが言及したことのどれでもすることができます。試したこととうまくいかなかったことを示すために共有したいコードはありますか?Webサイト全体を単一のエンティティとして実装することも、複数のエンティティにすることもできます。localStorageを使用して、データまたは実際のDBを永続化するか、どちらも使用できません。アプリケーションとは、生きているアクティブなインスタンスを意味します。ほとんどの場合、これは1つ、つまりルートです。しかし、繰り返しになりますが、アプリケーションを実装する方法はたくさんあります。
ZekeDroid 2016年

回答:


88

ブラウザの更新後もreduxの状態を維持したい場合は、reduxミドルウェアを使用してこれを行うのが最善です。redux-persistredux-storageミドルウェアをチェックしてください。どちらも、reduxの状態を保存するという同じタスクを実行して、自由に保存およびロードできるようにします。

-

編集

私がこの質問を再検討してからしばらく経ちましたが、他の質問(より賛成の回答ではありますが)があなた自身の解決策を転がすことを奨励しているのを見て、私はこれにもう一度答えると思いました。

この編集の時点で、両方のライブラリが過去6か月以内に更新されています。私のチームは、数年前から本番環境でredux-persistを使用しており、問題はありませんでした。

単純な問題のように見えるかもしれませんが、独自のソリューションをローリングすると、メンテナンスの負担が発生するだけでなく、バ​​グやパフォーマンスの問題が発生することがすぐにわかります。頭に浮かぶ最初の例は次のとおりです。

  1. JSON.stringifyまた、JSON.parse不要なときにパフォーマンスを低下させるだけでなく、reduxストアなどの重要なコードで処理しないとアプリケーションがクラッシュする可能性があるエラーをスローする可能性があります。
  2. (以下の回答で部分的に言及されています):アプリの状態をいつどのように保存および復元するかを理解することは簡単な問題ではありません。頻繁に行うと、パフォーマンスが低下します。十分ではないか、状態の間違った部分が続く場合は、さらに多くのバグが発生する可能性があります。上記のライブラリは、そのアプローチでバトルテストが行​​われ、動作をカスタマイズするための非常に確実な方法を提供します。
  3. reduxの美しさの一部(特にReactエコシステム)は、複数の環境に配置できることです。この編集の時点で、redux-persistには15の異なるストレージ実装があり、Web用の素晴らしいlocalForageライブラリや、React Native、Electron、Nodeのサポートが含まれています。

要約すると、3kBの縮小+ gzip圧縮(この編集の時点)の場合、これは問題ではありません。チームに解決を依頼します。


5
redux-persistをお勧めします(まだredux-storageを試していません)が、構成とセットアップがほとんどなくても、かなりうまく機能します。
larrydahooster 2016年

この日付の時点で、両方のライブラリは死んでいて、2年前までの最後のコミットで維持されていません。
AnBisw

1
-持続Reduxのようなルックスは、新しい私の執筆時点では22日前に公開して、バックビットである
Zeragamba

Reduxのストレージの新しい場所があるgithub.com/react-stack/redux-storage
マイケルFreidgeim

2
この回答についての注意:実際には、ソフトウェアとライブラリは、プログラミング言語の非常に重要なモジュールでさえサードパーティ/ライブラリによってサポートされるコミュニティ(サポート)ベースのアプローチを一般的に採用しています。一般に、開発者は、スタックで使用されているすべてのツールを監視して、ツールが非推奨/更新されているかどうかを確認する必要があります。2つの選択肢。1.独自の実装を行い、パフォーマンスとクロスプラットフォーム標準を保証するために永遠に開発を続けます。2.使用戦いテストされたソリューション&MiFreidgeimSO-stopbeingevilは言う@としてのみアップデート/勧告をチェック
オタクガイ

80

2019年8月25日編集

コメントの1つで述べられているように。元のredux-storageパッケージはreact-stackに移動されました。このアプローチは、独自の状態管理ソリューションの実装に引き続き焦点を当てています。


元の回答

提供された回答はある時点で有効でしたが、元のredux-storageパッケージが非推奨になり、保守されなくなったことに注意することが重要です...

パッケージredux-storageの元の作成者は、プロジェクトを非推奨にすることを決定し、現在は維持されていません。

さて、将来このような問題を回避するために他のパッケージに依存したくない場合は、独自のソリューションをロールバックするのは非常に簡単です。

あなたがする必要があるのは:

1-ストアをハイドレイトするために、2番目のパラメーターで状態を返す関数を作成し、localStorageその状態をcreateStore'sredux関数に渡します。

 const store = createStore(appReducers, state);

2-状態の変化をリッスンし、状態が変化するたびに、状態を localStorage

store.subscribe(() => {
    //this is just a function that saves state to localStorage
    saveState(store.getState());
}); 

そして、それだけです...私は実際に本番環境で同様のものを使用していますが、関数を使用する代わりに、以下のような非常に単純なクラスを作成しました...

class StateLoader {

    loadState() {
        try {
            let serializedState = localStorage.getItem("http://contoso.com:state");

            if (serializedState === null) {
                return this.initializeState();
            }

            return JSON.parse(serializedState);
        }
        catch (err) {
            return this.initializeState();
        }
    }

    saveState(state) {
        try {
            let serializedState = JSON.stringify(state);
            localStorage.setItem("http://contoso.com:state", serializedState);

        }
        catch (err) {
        }
    }

    initializeState() {
        return {
              //state object
            }
        };
    }
}

そして、アプリをブートストラップするとき...

import StateLoader from "./state.loader"

const stateLoader = new StateLoader();

let store = createStore(appReducers, stateLoader.loadState());

store.subscribe(() => {
    stateLoader.saveState(store.getState());
});

それが誰かを助けることを願っています

パフォーマンスノート

アプリケーションで状態の変更が頻繁に発生する場合、特にシリアル化/逆シリアル化する状態オブジェクトグラフが大きい場合は、ローカルストレージに保存する頻度が高すぎると、アプリケーションのパフォーマンスが低下する可能性があります。このような場合はRxJslodashなどを使用して状態をlocalStorageに保存する関数をデバウンスまたはスロットルすることができます。


11
ミドルウェアを使用する代わりに、私はこのアプローチを好みます。パフォーマンスの問題に関するヒントをありがとう。
ジョー州2018年

1
間違いなく好ましい答えです。ただし、ページを更新し、ストアの作成時にローカルストレージから状態を読み込むと、「レデューサーが受け取った以前の状態で見つかった予期しないプロパティ[コンテナー名]。次のいずれかが見つかると予想されます」というテキストを含むいくつかの警告が表示されます。代わりに、既知のレデューサープロパティ名: "global"、 "language"。予期しないプロパティは無視されます。それでも機能し、ストアを作成する時点で、他のすべてのコンテナーについて認識していないと基本的に不平を言っています。この警告を回避する方法は?
Zief 2018年

@Ziefは言うのが難しい。メッセージは非常に明確に「見えます」、レデューサーは指定されていないプロパティを期待しています。シリアル化された状態にデフォルト値を提供することに関連している可能性がありますか?
レオ

非常に簡単なソリューション。ありがとうございました。
IsharaMadawa19年

1
@Joezhouは、なぜこのアプローチを好むのか聞いてみたいと思います。個人的には、これはミドルウェアが意図したものとまったく同じように思われます。
michaelgmcd

1

これは、Leoの回答に基づいています(サードパーティのライブラリを使用せずに質問の目的を達成するため、受け入れられた回答である必要があります)。

Reduxストア作成し、ローカルストレージを使用して永続化し、ゲッターを介してストアに簡単にアクセスできるシングルトンクラスを作成しました

これを使用するには、メインクラスの周りに次のRedux-Provider要素を配置するだけです。

// ... Your other imports
import PersistedStore from "./PersistedStore";

ReactDOM.render(
  <Provider store={PersistedStore.getDefaultStore().store}>
    <MainClass />
  </Provider>,
  document.getElementById('root')
);

次のクラスをプロジェクトに追加します。

import {
  createStore
} from "redux";

import rootReducer from './RootReducer'

const LOCAL_STORAGE_NAME = "localData";

class PersistedStore {

  // Singleton property
  static DefaultStore = null;

  // Accessor to the default instance of this class
  static getDefaultStore() {
    if (PersistedStore.DefaultStore === null) {
      PersistedStore.DefaultStore = new PersistedStore();
    }

    return PersistedStore.DefaultStore;
  }

  // Redux store
  _store = null;

  // When class instance is used, initialize the store
  constructor() {
    this.initStore()
  }

  // Initialization of Redux Store
  initStore() {
    this._store = createStore(rootReducer, PersistedStore.loadState());
    this._store.subscribe(() => {
      PersistedStore.saveState(this._store.getState());
    });
  }

  // Getter to access the Redux store
  get store() {
    return this._store;
  }

  // Loading persisted state from localStorage, no need to access
  // this method from the outside
  static loadState() {
    try {
      let serializedState = localStorage.getItem(LOCAL_STORAGE_NAME);

      if (serializedState === null) {
        return PersistedStore.initialState();
      }

      return JSON.parse(serializedState);
    } catch (err) {
      return PersistedStore.initialState();
    }
  }

  // Saving persisted state to localStorage every time something
  // changes in the Redux Store (This happens because of the subscribe() 
  // in the initStore-method). No need to access this method from the outside
  static saveState(state) {
    try {
      let serializedState = JSON.stringify(state);
      localStorage.setItem(LOCAL_STORAGE_NAME, serializedState);
    } catch (err) {}
  }

  // Return whatever you want your initial state to be
  static initialState() {
    return {};
  }
}

export default PersistedStore;

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.