Как я могу сохранить дерево состояний редукции при обновлении?
Первым директором документации Redux является:
Состояние всего вашего приложения хранится в дереве объектов в одном хранилище.
И я действительно думал, что хорошо понимаю всех директоров.
Но теперь я запутался в том, что означает приложение.
Если приложение означает только одну небольшую сложную часть на веб-сайте и работает только на одной странице, я понимаю. Но что, если приложение означает весь сайт? Должен ли я использовать LocalStorage или cookie или что-то для сохранения дерева состояний? но что делать, если браузер не поддерживает LocalStorage?
Я хочу знать, как разработчики сохраняют свое дерево состояний!:)
Ответы
Ответ 1
Если вы хотите сохранить свое состояние редукса при обновлении браузера, лучше всего сделать это с помощью промежуточного программного обеспечения редукса. Ознакомьтесь с промежуточным программным обеспечением redux-persist и redux-storage. Они оба пытаются выполнить одну и ту же задачу по сохранению вашего избыточного состояния, чтобы оно могло быть сохранено и загружено по желанию.
-
Изменить
Прошло какое-то время с тех пор, как я вернулся к этому вопросу, но, увидев, что другой (хотя и более одобренный ответ) поощряет выработку собственного решения, я решил ответить на этот вопрос снова.
На момент редактирования обе библиотеки были обновлены в течение последних шести месяцев. В течение нескольких лет моя команда использовала Redux-Persist в производстве и не имела проблем.
Хотя это может показаться простой проблемой, вы быстро обнаружите, что развертывание вашего собственного решения не только приведет к бремени обслуживания, но и приведет к ошибкам и проблемам с производительностью. Первые примеры, которые приходят на ум:
JSON.stringify
и JSON.parse
могут не только снижать производительность, когда в этом нет необходимости, но и генерировать ошибки, которые, если их не обработать в критически важном фрагменте кода, таком как ваше хранилище с избыточностью, могут привести к сбою приложения.
- (Частично упомянуто в ответе ниже): выяснить, когда и как сохранять и восстанавливать состояние вашего приложения, - непростая проблема. Делайте это слишком часто, и вы ухудшите производительность. Недостаточно, или, если неправильные части состояния сохраняются, вы можете столкнуться с большим количеством ошибок. Библиотеки, упомянутые выше, проверены в бою своим подходом и предоставляют некоторые довольно надежные способы настройки их поведения.
- Частью красоты редукса (особенно в экосистеме 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
.