Bagaimana cara mempertahankan pohon status redux saat refresh?

92

Prinsip pertama dari dokumentasi Redux adalah:

Status seluruh aplikasi Anda disimpan dalam pohon objek dalam satu penyimpanan.

Dan saya benar-benar berpikir bahwa saya memahami semua kepala sekolah dengan baik. Tapi sekarang saya bingung apa artinya aplikasi.

Jika aplikasi berarti hanya satu dari bagian kecil yang rumit dalam situs web dan bekerja hanya dalam satu halaman, saya mengerti. Tetapi bagaimana jika aplikasi berarti seluruh situs web? Haruskah saya menggunakan LocalStorage atau cookie atau sesuatu untuk menjaga pohon status? tetapi bagaimana jika browser tidak mendukung LocalStorage?

Saya ingin tahu bagaimana pengembang mempertahankan pohon negara mereka! :)

incleaf
sumber
2
Itu pertanyaan yang luas. Anda dapat melakukan semua hal yang Anda sebutkan. Apakah Anda memiliki kode yang ingin Anda bagikan untuk menunjukkan kepada kami apa yang Anda coba dan tidak berhasil? Anda dapat menerapkan seluruh situs web Anda menjadi satu entitas, atau Anda dapat memiliki beberapa. Anda dapat menggunakan localStorage untuk menyimpan data, atau DB nyata, atau tidak keduanya. Penerapan berarti hidup, contoh aktif. Dalam kebanyakan kasus, ini hanya satu, root Anda. Tapi, sekali lagi, ada banyak cara untuk mengimplementasikan aplikasi.
ZekeDroid

Jawaban:

88

Jika Anda ingin mempertahankan status redux Anda saat browser menyegarkan, sebaiknya lakukan ini menggunakan middleware redux. Lihat middleware redux-persist dan redux-storage . Keduanya mencoba menyelesaikan tugas yang sama untuk menyimpan status redux Anda sehingga dapat disimpan dan dimuat sesuka hati.

-

Edit

Sudah beberapa lama sejak saya meninjau kembali pertanyaan ini, tetapi melihat bahwa yang lain (meskipun jawaban yang lebih disukai) mendorong untuk menggulirkan solusi Anda sendiri, saya pikir saya akan menjawab ini lagi.

Pada hasil edit ini, kedua perpustakaan telah diperbarui dalam enam bulan terakhir. Tim saya telah menggunakan redux-persist dalam produksi selama beberapa tahun sekarang dan tidak mengalami masalah.

Meskipun ini mungkin tampak seperti masalah sederhana, Anda akan segera menemukan bahwa meluncurkan solusi Anda sendiri tidak hanya akan menyebabkan beban pemeliharaan, tetapi juga mengakibatkan bug dan masalah kinerja. Contoh pertama yang terlintas dalam pikiran adalah:

  1. JSON.stringifydan JSON.parsetidak hanya dapat merusak kinerja saat tidak diperlukan, tetapi juga menimbulkan kesalahan yang jika tidak ditangani dalam bagian kode penting seperti toko redux Anda dapat merusak aplikasi Anda.
  2. (Sebagian disebutkan dalam jawaban di bawah): Mencari tahu kapan dan bagaimana cara menyimpan dan memulihkan status aplikasi Anda bukanlah masalah yang mudah. Lakukan terlalu sering dan Anda akan merusak kinerja. Tidak cukup, atau jika bagian status yang salah tetap ada, Anda mungkin menemukan diri Anda dengan lebih banyak bug. Library yang disebutkan di atas telah diuji pertempuran dalam pendekatannya dan menyediakan beberapa cara yang sangat mudah untuk menyesuaikan perilaku mereka.
  3. Bagian dari keindahan redux (terutama di ekosistem React) adalah kemampuannya untuk ditempatkan di berbagai lingkungan. Pada pengeditan ini, redux-persist memiliki 15 implementasi penyimpanan yang berbeda , termasuk pustaka localForage untuk web yang luar biasa, serta dukungan untuk React Native, Electron, dan Node.

Singkatnya, untuk 3kB diperkecil + gzip (pada saat pengeditan ini) ini bukan masalah, saya akan meminta tim saya untuk menyelesaikannya sendiri.

michaelgmcd
sumber
5
Saya dapat merekomendasikan redux-persist (belum mencoba redux-storage) tetapi berfungsi cukup baik untuk saya hanya dengan sedikit konfigurasi dan pengaturan.
larrydahooster
Pada tanggal ini kedua perpustakaan tersebut telah mati dan tidak dipertahankan dengan komitmen terakhir sejauh 2 tahun yang lalu.
AnBisw
1
Sepertinya redux-persist kembali sedikit, dengan terbitan baru 22 hari yang lalu pada saat saya menulis
Zeragamba
Lokasi baru penyimpanan redux
Michael Freidgeim
2
CATATAN INI TENTANG JAWABAN INI: Kenyataannya adalah bahwa perangkat lunak & perpustakaan umumnya mengadopsi pendekatan berbasis komunitas (dukungan) yang bahkan beberapa modul yang sangat penting dari bahasa pemrograman didukung oleh pihak ketiga / perpustakaan. Umumnya, pengembang harus mengawasi setiap alat yang digunakan dalam tumpukannya untuk mengetahui apakah alat itu sudah usang / diperbarui atau tidak. Dua pilihan; 1. Terapkan milik Anda sendiri & terus berkembang selamanya untuk memastikan kinerja & standar lintas platform. 2. Gunakan solusi yang telah teruji pertempuran & hanya periksa pembaruan / rekomendasi seperti yang dikatakan @ MiFreidgeimSO-stopbeingevil
Geek Guy
80

Edit 25 Agustus 2019

Seperti yang tertera di salah satu komentar. Paket penyimpanan redux asli telah dipindahkan ke react-stack . Pendekatan ini masih berfokus pada penerapan solusi pengelolaan negara Anda sendiri.


Jawaban Asli

Meskipun jawaban yang diberikan valid di beberapa titik, penting untuk diperhatikan bahwa paket penyimpanan redux asli telah usang dan tidak lagi dipertahankan ...

Penulis asli paket redux-storage telah memutuskan untuk menghentikan proyek dan tidak lagi dipertahankan.

Sekarang, jika Anda tidak ingin memiliki ketergantungan pada paket lain untuk menghindari masalah seperti ini di masa mendatang, sangat mudah untuk menggulung solusi Anda sendiri.

Yang perlu Anda lakukan hanyalah:

1- Buat fungsi yang mengembalikan status dari localStoragedan kemudian meneruskan status ke createStorefungsi reduks di parameter kedua untuk menghidrasi penyimpanan

 const store = createStore(appReducers, state);

2- Dengarkan perubahan status dan setiap kali status berubah, simpan status ke localStorage

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

Dan hanya itu ... Saya sebenarnya menggunakan sesuatu yang serupa dalam produksi, tetapi alih-alih menggunakan fungsi, saya menulis kelas yang sangat sederhana seperti di bawah ini ...

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

dan kemudian saat melakukan bootstrap aplikasi Anda ...

import StateLoader from "./state.loader"

const stateLoader = new StateLoader();

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

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

Semoga bisa membantu seseorang

Catatan Kinerja

Jika perubahan status sangat sering terjadi dalam aplikasi Anda, menyimpan ke penyimpanan lokal terlalu sering dapat merusak kinerja aplikasi Anda, terutama jika grafik objek status untuk membuat serial / deserialisasi besar. Untuk kasus ini, Anda mungkin ingin membatalkan atau membatasi fungsi yang menyimpan status ke localStorage menggunakan RxJs, lodashatau yang serupa.

Leo
sumber
11
Daripada menggunakan middleware, saya lebih memilih pendekatan ini. Terima kasih atas tip yang berkaitan dengan kinerja.
Joe zhou
1
Pasti jawaban yang disukai. Namun, saat saya menyegarkan halaman dan memuat status dari penyimpanan lokal saat membuat penyimpanan, saya mendapatkan beberapa peringatan yang menyertakan teks "Properti tak terduga [nama wadah] ditemukan dalam keadaan sebelumnya yang diterima oleh peredam. Diharapkan menemukan salah satu nama properti peredam yang dikenal sebagai gantinya: "global", "bahasa". Properti tak terduga akan diabaikan. Ini masih berfungsi, dan pada dasarnya mengeluh bahwa pada saat membuat penyimpanan, ia tidak tahu tentang semua penampung lain itu. Apakah ada cara mengatasi peringatan ini?
Zief
@Zief sulit untuk dikatakan. Pesan "tampaknya" cukup jelas, reduksi mengharapkan properti yang tidak ditentukan. Mungkin ada sesuatu yang terkait dengan memberikan nilai default ke status serial?
Leo
Solusi yang sangat mudah. Terima kasih.
Ishara Madawa
1
@Joezhou ingin sekali mendengar mengapa Anda lebih memilih pendekatan ini. Secara pribadi, ini sepertinya hal yang tepat untuk middleware.
michaelgmcd
1

Ini didasarkan pada jawaban Leo (yang seharusnya merupakan jawaban yang diterima karena dapat mencapai tujuan pertanyaan tanpa menggunakan libs pihak ketiga).

Saya telah membuat kelas Singleton yang membuat Redux Store, mempertahankannya menggunakan penyimpanan lokal dan memungkinkan akses mudah ke tokonya melalui getter .

Untuk menggunakannya, cukup letakkan elemen Redux-Provider berikut di sekitar kelas utama Anda:

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

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

dan tambahkan kelas berikut ke proyek Anda:

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;

thgc
sumber