Як я можу зберегти дерево станів відновлення при оновленні?


92

Першим принципом документації Redux є:

Стан усієї вашої програми зберігається у дереві об’єктів в межах одного сховища.

І я насправді думав, що добре розумію всіх директорів. Але мене зараз бентежить, що означає додаток.

Якщо додаток означає лише одну з трохи складних частин веб-сайту і працює лише на одній сторінці, я розумію. Але що, якщо додаток означає цілий веб-сайт? Чи слід використовувати LocalStorage, cookie чи щось інше для збереження дерева стану? але що, якщо браузер не підтримує LocalStorage?

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


2
Це досить широке питання. Ви можете робити будь-що з того, що згадали. У вас є код, яким ви хотіли б поділитися, щоб показати нам, що ви пробували, а що не працювало? Ви можете реалізувати весь свій веб-сайт як єдине ціле, а можете мати декілька. Ви можете використовувати localStorage для збереження даних, або реальної БД, або жодного. Додаток означає живу, активну інстанцію. У більшості випадків це лише один, ваш корінь. Але знову ж таки, існує безліч способів реалізації додатків.
— ZekeDroid

Відповіді:


88

Якщо ви хочете зберегти стан відновлення в процесі оновлення браузера, найкраще це зробити за допомогою проміжного програмного забезпечення redux. Перевірте проміжне програмне забезпечення redux-persist та redux-storage . Вони обидва намагаються виконати одне і те ж завдання - зберегти ваш стан відновлення, щоб його можна було зберегти та завантажити за бажанням.

-

Редагувати

Минув деякий час з того часу, як я знову переглядав це питання, але, побачивши, що інша (хоча і більш прихильна відповідь) заохочує пропонувати своє власне рішення, я вирішив відповісти на це ще раз.

На момент редагування обидві бібліотеки були оновлені протягом останніх шести місяців. Моя команда використовує redux-persist у виробництві вже кілька років і не мала жодних проблем.

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

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

Підводячи підсумок, для 3kB мініфікованих + gzipped (на момент цього редагування) це не проблема, я б попросив мою команду вирішити сама.


5
Я можу порекомендувати redux-persist (ще не пробував redux-storage), але це працює для мене досить добре, дуже мало конфігурації та налаштування.
— larrydahooster

Станом на цю дату обидві бібліотеки здаються мертвими і не підтримуються з останніми комітами ще 2 роки тому.
— AnBisw,

1
виглядає так, як redux-persist повернувся трохи, з новою публікацією 22 дні тому під час мого написання
— Zeragamba

Новим місцем зберігання redux є github.com/react-stack/redux-storage
— Michael Freidgeim

2
ПРИМІТКА ПРО ЦЕ ЦЕ ВІДПОВІДЬ: Реальність така, що програмне забезпечення та бібліотеки загалом прийняли підхід, заснований на співтоваристві, що навіть деякі дуже важливі модулі мови програмування підтримуються третіми сторонами / бібліотеками. Як правило, розробник повинен стежити за кожним інструментом, що використовується у його стеку, щоб знати, чи він не застосовується / оновлюється чи ні. Два варіанти; 1. Впроваджуйте власні та продовжуйте розвиватися назавжди, забезпечуючи ефективність та крос-платформні стандарти. 2. Використовуйте перевірене на боях рішення і перевіряйте лише оновлення / рекомендації, як каже @ MiFreidgeimSO-stopbeingevil
— Geek Guy

80

Редагувати 25 серпня 2019 р

Як зазначено в одному з коментарів. Оригінальний пакет редукційного зберігання переміщено в стек реакцій . Цей підхід досі зосереджений на впровадженні власного рішення щодо управління державою.


Оригінальна відповідь

Незважаючи на те, що надана відповідь була дійсною в певний момент, важливо зауважити, що оригінальний пакет редукційного сховища застарів та більше не підтримується ...

Оригінальний автор пакета 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або що - щось подібне.


11
Замість використання проміжного програмного забезпечення я віддаю перевагу такому підходу. Дякуємо за поради, що стосуються продуктивності.
— Джо Чжоу

1
Безумовно, краща відповідь. Однак, коли я оновлюю сторінку, і вона створює стан з localstorage при створенні магазину, я отримую кілька попереджень, які містять текст "Несподівані властивості [імена контейнерів], знайдені в попередньому стані, отримані редуктором. Очікується, що знайдеться одне з відомі імена властивостей редуктора замість цього: "глобальний", "мова". Несподівані властивості будуть ігноруватися. Він все ще працює і в основному скаржиться, що на момент створення магазину він не знає про всі ці інші контейнери. як обійти це попередження?
— Зіф,

@Zief важко сказати. Повідомлення "здається" цілком зрозумілим, редуктори очікують властивостей, які не вказані. Це може бути щось пов’язане із наданням значень за замовчуванням для серіалізованого стану?
— Лев

Дуже пряме рішення. Дякую.
— Ishara Madawa

1
@ Joezhou хотів би почути, чому ви віддаєте перевагу такому підходу. Особисто мені здається, що саме для цього було призначено проміжне програмне забезпечення.
— michaelgmcd

1

Це ґрунтується на відповіді Лева (яка повинна бути прийнятою відповіддю, оскільки вона досягає мети запитання, не використовуючи жодних інших сторін).

Я створив клас 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;

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.