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


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.