Как я могу сохранить дерево состояний редукции при обновлении?

Первым директором документации Redux является:

Состояние всего вашего приложения хранится в дереве объектов в одном хранилище.

И я действительно думал, что хорошо понимаю всех директоров. Но теперь я запутался в том, что означает приложение.

Если приложение означает только одну небольшую сложную часть на веб-сайте и работает только на одной странице, я понимаю. Но что, если приложение означает весь сайт? Должен ли я использовать LocalStorage или cookie или что-то для сохранения дерева состояний? но что делать, если браузер не поддерживает LocalStorage?

Я хочу знать, как разработчики сохраняют свое дерево состояний!:)

Ответ 1

Если вы хотите сохранить свое состояние редукса при обновлении браузера, лучше всего сделать это с помощью промежуточного программного обеспечения редукса. Ознакомьтесь с промежуточным программным обеспечением redux-persist и redux-storage. Они оба пытаются выполнить одну и ту же задачу по сохранению вашего избыточного состояния, чтобы оно могло быть сохранено и загружено по желанию.

-

Изменить

Прошло какое-то время с тех пор, как я вернулся к этому вопросу, но, увидев, что другой (хотя и более одобренный ответ) поощряет выработку собственного решения, я решил ответить на этот вопрос снова.

На момент редактирования обе библиотеки были обновлены в течение последних шести месяцев. В течение нескольких лет моя команда использовала Redux-Persist в производстве и не имела проблем.

Хотя это может показаться простой проблемой, вы быстро обнаружите, что развертывание вашего собственного решения не только приведет к бремени обслуживания, но и приведет к ошибкам и проблемам с производительностью. Первые примеры, которые приходят на ум:

  1. JSON.stringify и JSON.parse могут не только снижать производительность, когда в этом нет необходимости, но и генерировать ошибки, которые, если их не обработать в критически важном фрагменте кода, таком как ваше хранилище с избыточностью, могут привести к сбою приложения.
  2. (Частично упомянуто в ответе ниже): выяснить, когда и как сохранять и восстанавливать состояние вашего приложения, - непростая проблема. Делайте это слишком часто, и вы ухудшите производительность. Недостаточно, или, если неправильные части состояния сохраняются, вы можете столкнуться с большим количеством ошибок. Библиотеки, упомянутые выше, проверены в бою своим подходом и предоставляют некоторые довольно надежные способы настройки их поведения.
  3. Частью красоты редукса (особенно в экосистеме React) является его способность размещаться в нескольких средах. На момент этого редактирования, redux-persist имеет 15 различных реализаций хранилища, включая потрясающую библиотеку localForage для веб-сайтов, а также поддержку React Native, Electron и Node.

Подводя итог, для 3kB minified + gzipped (на момент этого редактирования) это не проблема, я бы попросил свою команду решить сам.

Ответ 2

Изменить 25 августа 2019 года

Как указано в одном из комментариев. Исходный пакет избыточного хранилища был перемещен в стек реакции. Этот подход по-прежнему ориентирован на реализацию собственного решения для управления состоянием.


Оригинальный ответ

Хотя предоставленный ответ в какой-то момент был действительным, важно отметить, что оригинальный пакет redux-storage устарел и больше не поддерживается...

Первоначальный автор пакета redux-storage решил отказаться от проекта и больше не поддерживается.

Теперь, если вы не хотите иметь зависимости от других пакетов, чтобы избежать подобных проблем в будущем, очень легко найти собственное решение.

Все, что вам нужно сделать, это:

1- Создайте функцию, которая возвращает состояние из localStorage, а затем передайте состояние в функцию редукции createStore во втором параметре для увлажнения хранилища

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

Надеюсь, это кому-нибудь поможет

Примечание о производительности

Если изменения состояния очень часты в вашем приложении, слишком частое сохранение в локальное хранилище может снизить производительность вашего приложения, особенно если граф объектов состояния для сериализации/десериализации большой. В этих случаях вы можете захотеть отменить или уменьшить функцию, которая сохраняет состояние в localStorage, используя RxJs, lodash или что-то подобное.

Ответ 3

Это основано на ответе Лео (который должен быть принятым ответом, поскольку он достигает цели вопроса без использования сторонних библиотек).

Я создал класс Singleton, который создает Redux Store, сохраняет его с помощью локального хранилища и предоставляет простой доступ к его хранилищу через геттер.

Чтобы использовать его, просто поместите следующий элемент 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;

Ответ 4

Оригинальный вопрос:

Я хочу знать, как разработчики сохраняют свое дерево состояний!:)

Ну, так как я не вижу этого ответа, и вы использовали ярлык React; часто React Webapps реализованы как SPA (одностраничное приложение), которое на самом деле не обновляет браузер. Они могут сделать это, как если бы это произошло, изменив URL-адрес, который вы видите в адресной строке. Популярный способ сделать это - react-router.

Но все же состояние SPA не сохранилось в обновлении браузера, так что при входе redux-persist.