web-dev-qa-db-fra.com

Comment puis-je conserver l'arbre d'état redux lors de l'actualisation?

Le premier principe de la documentation Redux est:

L'état de votre application entière est stocké dans une arborescence d'objets au sein d'un magasin unique.

Et je pensais en fait que je comprenais bien tous les principes. Mais je ne comprends plus ce que veut dire application.

Je comprends que si l’application ne représente qu’une partie peu compliquée du site Web et ne fonctionne que sur une page. Mais que se passe-t-il si application signifie site entier? Devrais-je utiliser LocalStorage ou un cookie ou quelque chose pour conserver l'arbre d'état? mais que se passe-t-il si le navigateur ne prend pas en charge LocalStorage?

Je veux savoir comment les développeurs conservent leur arbre d'état! :)

55
incleaf

Si vous souhaitez conserver votre état redux lors d'une actualisation du navigateur, utilisez de préférence le middleware redux. Découvrez les middleware redux-persist et redux-storage . Ils essaient tous les deux d'accomplir la même tâche de stockage de votre état redux afin qu'il puisse être enregistré et chargé à volonté.

-

Modifier

Cela fait quelque temps que je n'ai pas revisité cette question, mais vu que l'autre (bien que la réponse soit plus votée) encourage la solution de rechange, je me suis dit que j'y répondrais de nouveau.

Au moment de cette modification, les deux bibliothèques ont été mises à jour au cours des six derniers mois. Mon équipe utilise redux-persist en production depuis quelques années et n’a rencontré aucun problème.

Bien que cela puisse sembler être un problème simple, vous constaterez rapidement que le déploiement de votre propre solution non seulement entraînera une charge de maintenance, mais entraînera également des bugs et des problèmes de performances. Les premiers exemples qui viennent à l’esprit sont:

  1. JSON.stringify et JSON.parse peuvent non seulement nuire aux performances lorsqu'ils ne sont pas nécessaires, mais également générer des erreurs qui, lorsqu'elles ne sont pas gérées dans un élément de code critique comme votre magasin redux, peuvent provoquer le blocage de votre application.
  2. (En partie mentionné dans la réponse ci-dessous): Déterminer quand et comment enregistrer et restaurer l'état de votre application n'est pas un problème simple. Faites-le trop souvent et vous allez nuire à la performance. Pas assez, ou si les mauvaises parties de l'état persistent, vous pouvez vous retrouver avec plus de bugs. Les bibliothèques mentionnées ci-dessus ont fait leurs preuves dans leur approche et fournissent quelques moyens relativement simples de personnaliser leur comportement.
  3. Une partie de la beauté de redux (en particulier dans l’écosystème React, c’est sa capacité à être placée dans plusieurs environnements. À compter de cette édition, redux-persist a 15 implémentations de stockage différentes , y compris awesome bibliothèque localForage pour le Web, ainsi que la prise en charge de React Native, Electron et Node.

Pour résumer, pour 2,5 ko minifiés + gzippés ce n’est pas un problème, je demanderais à mon équipe de se résoudre elle-même.

54
michaelgmcd

Modifier le 25 août 2019

Comme indiqué dans l'un des commentaires. Le paquetage original redux-storage a été déplacé vers react-stack . Cette approche reste axée sur la mise en œuvre de votre propre solution de gestion d’état.


Réponse originale

Bien que la réponse fournie soit valide à un moment donné, il est important de noter que le paquet d'origine redux-storage est obsolète et qu'il n'est plus mis à jour ...

L'auteur original du paquetage redux-storage a décidé de désapprouver le projet et n'est plus maintenu.

Maintenant, si vous ne voulez pas avoir de dépendances sur d’autres paquets pour éviter de tels problèmes à l’avenir, il est très facile de lancer votre propre solution.

Tout ce que vous devez faire c'est:

1- Créez une fonction qui retourne l'état de localStorage, puis passez l'état à la fonction redux de createStore dans le deuxième paramètre afin d'hydrater le magasin.

 const store = createStore(appReducers, state);

2- Ecoutez les changements d'état et chaque fois que l'état change, enregistrez l'état sur localStorage

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

Et c'est tout ... J'utilise quelque chose de similaire en production, mais au lieu d'utiliser des fonctions, j'ai écrit un cours très simple comme ci-dessous ...

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

et puis quand amorçant votre application ...

import StateLoader from "./state.loader"

const stateLoader = new StateLoader();

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

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

J'espère que ça aide quelqu'un

Note de performance

Si les changements d'état sont très fréquents dans votre application, l'enregistrement trop souvent sur un stockage local risque de nuire aux performances de votre application, en particulier si le graphe d'objet d'état à sérialiser/désérialiser est volumineux. Dans ces cas, vous voudrez peut-être atténuer ou limiter la fonction qui enregistre l'état dans localStorage en utilisant RxJs, lodash ou quelque chose de similaire.

70
Leo

Ceci est basé sur la réponse de Leo (qui devrait être la réponse acceptée car elle permet d'atteindre l'objectif de la question sans utiliser de bibliothèque tierce).

J'ai créé une classe Singleton qui crée un magasin Redux, la conserve à l'aide d'un stockage local et permet un accès simple à son magasin via un getter.

Pour l'utiliser, il suffit de placer l'élément suivant Redux-Provider autour de votre classe principale:

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

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

et ajoutez la classe suivante à votre projet:

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;
0
thgc