Wie kann ich den Redux-Statusbaum beim Aktualisieren beibehalten?

91

Das erste Prinzip der Redux-Dokumentation lautet:

Der Status Ihrer gesamten Anwendung wird in einem Objektbaum in einem einzelnen Speicher gespeichert.

Und ich dachte tatsächlich, dass ich alle Prinzipien gut verstehe. Aber ich bin jetzt verwirrt, was Anwendung bedeutet.

Wenn Anwendung nur einen der wenig komplizierten Teile der Website bedeutet und auf nur einer Seite funktioniert, verstehe ich. Aber was ist, wenn Anwendung ganze Website bedeutet? Sollte ich LocalStorage oder Cookie oder etwas anderes verwenden, um den Statusbaum zu behalten? Aber was ist, wenn der Browser LocalStorage nicht unterstützt?

Ich möchte wissen, wie Entwickler ihren Statusbaum behalten! :) :)

inkl
quelle
2
Das ist eine ziemlich breite Frage. Sie können alles tun, was Sie erwähnt haben. Haben Sie Code, den Sie teilen möchten, um uns zu zeigen, was Sie versucht haben und was nicht funktioniert hat? Sie können Ihre gesamte Website als eine Einheit implementieren, oder Sie können mehrere haben. Sie können localStorage verwenden, um Daten oder eine echte Datenbank oder keine zu speichern. Anwendung bedeutet lebendige, aktive Instanz. In den meisten Fällen ist dies nur eine, Ihre Wurzel. Aber auch hier gibt es viele Möglichkeiten, Anwendungen zu implementieren.
ZekeDroid

Antworten:

88

Wenn Sie Ihren Redux-Status während einer Browser-Aktualisierung beibehalten möchten, verwenden Sie am besten Redux-Middleware. Schauen Sie sich die Middleware für Redux-Persist und Redux-Storage an . Beide versuchen, dieselbe Aufgabe zu erfüllen, Ihren Redux-Status zu speichern, damit er nach Belieben gespeichert und geladen werden kann.

- -

Bearbeiten

Es ist einige Zeit her, seit ich diese Frage erneut aufgegriffen habe, aber da die andere (wenn auch positivere Antwort) dazu anregt, Ihre eigene Lösung zu entwickeln, dachte ich, ich würde sie erneut beantworten.

Zum Zeitpunkt dieser Bearbeitung wurden beide Bibliotheken in den letzten sechs Monaten aktualisiert. Mein Team verwendet Redux-Persist seit einigen Jahren in der Produktion und hatte keine Probleme.

Es scheint zwar ein einfaches Problem zu sein, aber Sie werden schnell feststellen, dass das Rolling Ihrer eigenen Lösung nicht nur einen Wartungsaufwand verursacht, sondern auch zu Fehlern und Leistungsproblemen führt. Die ersten Beispiele, die mir in den Sinn kommen, sind:

  1. JSON.stringifyund JSON.parsekann nicht nur die Leistung beeinträchtigen, wenn sie nicht benötigt wird, sondern auch Fehler auslösen, die, wenn sie in einem kritischen Code wie Ihrem Redux-Speicher nicht behandelt werden, Ihre Anwendung zum Absturz bringen können.
  2. (Teilweise in der Antwort unten erwähnt): Es ist kein einfaches Problem, herauszufinden, wann und wie Sie Ihren App-Status speichern und wiederherstellen können. Tun Sie es zu oft und Sie werden die Leistung beeinträchtigen. Nicht genug, oder wenn die falschen Teile des Staates bestehen bleiben, können Sie mit mehr Fehlern konfrontiert sein. Die oben genannten Bibliotheken sind in ihrem Ansatz kampferprobt und bieten einige ziemlich narrensichere Möglichkeiten, ihr Verhalten anzupassen.
  3. Ein Teil der Schönheit von Redux (insbesondere im React-Ökosystem) ist seine Fähigkeit, in mehreren Umgebungen platziert zu werden. Zum Zeitpunkt dieser Bearbeitung verfügt redux-persist über 15 verschiedene Speicherimplementierungen , einschließlich der fantastischen localForage-Bibliothek für das Web sowie Unterstützung für React Native, Electron und Node.

Zusammenfassend ist dies für 3kB minimiert + gezippt (zum Zeitpunkt dieser Bearbeitung) kein Problem. Ich würde mein Team bitten, sich selbst zu lösen.

michaelgmcd
quelle
5
Ich kann redux-persist empfehlen (habe Redux-Speicher noch nicht ausprobiert), aber es funktioniert ziemlich gut für mich mit nur sehr wenig Konfiguration und Einrichtung.
Larrydahooster
Ab diesem Datum sind beide Bibliotheken tot und werden nicht mehr mit den letzten Commits bereits vor 2 Jahren gewartet.
AnBisw
1
Es sieht so aus, als ob Redux-Persist ein bisschen zurück ist, mit einer neuen Veröffentlichung vor 22 Tagen zum Zeitpunkt meines Schreibens
Zeragamba
Der neue Standort von Redux-Storage ist github.com/react-stack/redux-storage
Michael Freidgeim
2
HINWEIS ZU DIESER ANTWORT: Die Realität ist, dass Software und Bibliotheken im Allgemeinen einen Community-basierten (Support-) Ansatz gewählt haben, bei dem sogar einige sehr wichtige Module einer Programmiersprache von Dritten / Bibliotheken unterstützt werden. Im Allgemeinen muss der Entwickler jedes in seinem Stapel verwendete Tool im Auge behalten, um festzustellen, ob es veraltet / aktualisiert wird oder nicht. Zwei Möglichkeiten; 1. Implementieren Sie Ihre eigenen und entwickeln Sie sich für immer weiter, um Leistung und plattformübergreifende Standards sicherzustellen. 2. Verwenden Sie eine kampferprobte Lösung und überprüfen Sie nur Aktualisierungen / Empfehlungen, wie @ MiFreidgeimSO-stopbeingevil sagt
Geek Guy
80

Bearbeiten 25-Aug-2019

Wie in einem der Kommentare angegeben. Das ursprüngliche Redux-Speicherpaket wurde in den React-Stack verschoben . Dieser Ansatz konzentriert sich weiterhin auf die Implementierung Ihrer eigenen Zustandsverwaltungslösung.


Ursprüngliche Antwort

Obwohl die angegebene Antwort irgendwann gültig war, ist es wichtig zu beachten, dass das ursprüngliche Redux-Speicherpaket veraltet ist und nicht mehr gewartet wird ...

Der ursprüngliche Autor des Pakets redux-storage hat beschlossen, das Projekt zu verwerfen und nicht mehr zu warten.

Wenn Sie keine Abhängigkeiten von anderen Paketen haben möchten, um solche Probleme in Zukunft zu vermeiden, ist es sehr einfach, Ihre eigene Lösung zu entwickeln.

Alles was Sie tun müssen ist:

1- Erstellen Sie eine Funktion, die den Status von zurückgibt, localStorageund übergeben Sie den Status dann an die createStoreRedux-Funktion im zweiten Parameter, um den Speicher mit Feuchtigkeit zu versorgen

 const store = createStore(appReducers, state);

2- Achten Sie auf Statusänderungen und speichern Sie den Status jedes Mal, wenn sich der Status ändert localStorage

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

Und das war's ... Ich verwende tatsächlich etwas Ähnliches in der Produktion, aber anstatt Funktionen zu verwenden, habe ich eine sehr einfache Klasse wie unten geschrieben ...

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
            }
        };
    }
}

und dann beim Bootstrapping Ihrer App ...

import StateLoader from "./state.loader"

const stateLoader = new StateLoader();

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

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

Hoffe es hilft jemandem

Leistungshinweis

Wenn Statusänderungen in Ihrer Anwendung sehr häufig sind, kann das zu häufige Speichern im lokalen Speicher die Leistung Ihrer Anwendung beeinträchtigen, insbesondere wenn das zu serialisierende / deserialisierende Statusobjektdiagramm groß ist. In diesen Fällen möchten Sie möglicherweise die Funktion, mit der der Status in localStorage gespeichert wird RxJs, lodashoder etwas Ähnliches entprellen oder drosseln .

Löwe
quelle
11
Anstatt Middleware zu verwenden, bevorzuge ich diesen Ansatz. Vielen Dank für die Tipps bezüglich Leistungsbedenken.
Joe Zhou
1
Auf jeden Fall die bevorzugte Antwort. Wenn ich die Seite jedoch aktualisiere und beim Erstellen des Speichers den Status aus localstorage lädt, werden mehrere Warnungen angezeigt, die den Text "Unerwartete Eigenschaften [Containernamen], die im vorherigen Status gefunden wurden und vom Reduzierer empfangen wurden. Es wird erwartet, dass einer der gefunden wird." stattdessen bekannte Reducer-Eigenschaftsnamen: "global", "language". Unerwartete Eigenschaften werden ignoriert. Es funktioniert immer noch und beschwert sich im Grunde, dass es zum Zeitpunkt der Erstellung des Geschäfts nicht über all diese anderen Container Bescheid weiß Weg um diese Warnung?
Zief
@ Zief schwer zu sagen. Die Meldung "scheint" ganz klar zu sein, die Reduzierer erwarten Eigenschaften, die nicht angegeben sind. Es könnte etwas mit der Bereitstellung von Standardwerten für den serialisierten Status zu tun haben?
Leo
Sehr einfache Lösung. Vielen Dank.
Ishara Madawa
1
@ Joezhou würde gerne hören, warum Sie diesen Ansatz bevorzugen. Persönlich scheint dies genau das zu sein, wofür Middleware gedacht war.
michaelgmcd
1

Dies basiert auf Leos Antwort (die die akzeptierte Antwort sein sollte, da sie den Zweck der Frage ohne Verwendung von Bibliotheken von Drittanbietern erfüllt).

Ich habe eine Singleton-Klasse erstellt , die einen Redux-Speicher erstellt, ihn unter Verwendung des lokalen Speichers beibehält und einen einfachen Zugriff auf den Speicher über einen Getter ermöglicht .

Um es zu verwenden, platzieren Sie einfach das folgende Redux-Provider-Element um Ihre Hauptklasse:

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

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

und fügen Sie Ihrem Projekt die folgende Klasse hinzu:

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;

thgc
quelle