Saya ingin bertahan beberapa bagian pohon negara saya ke Penyimpanan lokal. Apa tempat yang tepat untuk melakukannya? Peredam atau aksi?
Saya ingin bertahan beberapa bagian pohon negara saya ke Penyimpanan lokal. Apa tempat yang tepat untuk melakukannya? Peredam atau aksi?
Jawaban:
Peredam tidak pernah merupakan tempat yang tepat untuk melakukan ini karena peredam harus murni dan tidak memiliki efek samping.
Saya akan merekomendasikan hanya melakukannya di pelanggan:
store.subscribe(() => {
// persist your state
})
Sebelum membuat toko, baca bagian-bagian yang bertahan:
const persistedState = // ...
const store = createStore(reducer, persistedState)
Jika Anda menggunakan combineReducers()
Anda akan melihat bahwa reduksi yang belum menerima status akan "boot up" seperti biasa menggunakan standarnyastate
nilai argumen . Ini bisa sangat berguna.
Dianjurkan agar Anda debounce pelanggan Anda sehingga Anda tidak menulis ke localStorage terlalu cepat, atau Anda akan memiliki masalah kinerja.
Akhirnya, Anda dapat membuat middleware yang merangkumnya sebagai alternatif, tetapi saya akan mulai dengan pelanggan karena ini adalah solusi yang lebih sederhana dan melakukan pekerjaan dengan baik.
Untuk mengisi kekosongan jawaban Dan Abramov, Anda dapat menggunakan store.subscribe()
seperti ini:
store.subscribe(()=>{
localStorage.setItem('reduxState', JSON.stringify(store.getState()))
})
Sebelum membuat toko, periksa localStorage
dan uraikan JSON apa pun di bawah kunci Anda seperti ini:
const persistedState = localStorage.getItem('reduxState')
? JSON.parse(localStorage.getItem('reduxState'))
: {}
Anda kemudian meneruskan persistedState
konstanta ini ke createStore
metode Anda seperti ini:
const store = createStore(
reducer,
persistedState,
/* any middleware... */
)
persistedState
mengembalikan initialState
objek kosong? Kalau tidak, saya pikir createStore
akan menginisialisasi dengan objek kosong itu.
Dalam sebuah kata: middleware.
Lihat redux-persisten . Atau tulis sendiri.
[UPDATE 18 Des 2016] Diedit untuk menghapus penyebutan dua proyek serupa yang sekarang tidak aktif atau usang.
Jika ada yang mengalami masalah dengan solusi di atas, Anda dapat menulis sendiri. Mari saya tunjukkan apa yang saya lakukan. Abaikan saga middleware
hal-hal hanya fokus pada dua hal localStorageMiddleware
dan reHydrateStore
metode. yang localStorageMiddleware
menarik semua redux state
dan menempatkan dalam local storage
dan rehydrateStore
menarik semua applicationState
di penyimpanan lokal jika ada dan menempatkan dalamredux store
import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga';
import decoristReducers from '../reducers/decorist_reducer'
import sagas from '../sagas/sagas';
const sagaMiddleware = createSagaMiddleware();
/**
* Add all the state in local storage
* @param getState
* @returns {function(*): function(*=)}
*/
const localStorageMiddleware = ({getState}) => { // <--- FOCUS HERE
return (next) => (action) => {
const result = next(action);
localStorage.setItem('applicationState', JSON.stringify(
getState()
));
return result;
};
};
const reHydrateStore = () => { // <-- FOCUS HERE
if (localStorage.getItem('applicationState') !== null) {
return JSON.parse(localStorage.getItem('applicationState')) // re-hydrate the store
}
}
const store = createStore(
decoristReducers,
reHydrateStore(),// <-- FOCUS HERE
applyMiddleware(
sagaMiddleware,
localStorageMiddleware,// <-- FOCUS HERE
)
)
sagaMiddleware.run(sagas);
export default store;
localStorage
walaupun tidak ada yang berubah di toko? Bagaimana Anda mengimbangi penulisan yang tidak perlu
Saya tidak bisa menjawab @Gardezi tetapi opsi berdasarkan kodenya bisa:
const rootReducer = combineReducers({
users: authReducer,
});
const localStorageMiddleware = ({ getState }) => {
return next => action => {
const result = next(action);
if ([ ACTIONS.LOGIN ].includes(result.type)) {
localStorage.setItem(appConstants.APP_STATE, JSON.stringify(getState()))
}
return result;
};
};
const reHydrateStore = () => {
const data = localStorage.getItem(appConstants.APP_STATE);
if (data) {
return JSON.parse(data);
}
return undefined;
};
return createStore(
rootReducer,
reHydrateStore(),
applyMiddleware(
thunk,
localStorageMiddleware
)
);
perbedaannya adalah bahwa kami hanya menyimpan beberapa tindakan, Anda dapat menggunakan fungsi debounce untuk menyimpan hanya interaksi terakhir dari negara Anda
Saya agak terlambat tetapi saya menerapkan kondisi persisten sesuai dengan contoh yang dinyatakan di sini. Jika Anda ingin memperbarui negara hanya setiap X detik, pendekatan ini dapat membantu Anda:
Tentukan fungsi pembungkus
let oldTimeStamp = (Date.now()).valueOf()
const millisecondsBetween = 5000 // Each X milliseconds
function updateLocalStorage(newState)
{
if(((Date.now()).valueOf() - oldTimeStamp) > millisecondsBetween)
{
saveStateToLocalStorage(newState)
oldTimeStamp = (Date.now()).valueOf()
console.log("Updated!")
}
}
Panggil fungsi wrapper di pelanggan Anda
store.subscribe((state) =>
{
updateLocalStorage(store.getState())
});
Dalam contoh ini, negara diperbarui paling banyak setiap 5 detik, terlepas dari seberapa sering pembaruan dipicu.
(state) => { updateLocalStorage(store.getState()) }
di lodash.throttle
seperti ini: store.subscribe(throttle(() => {(state) => { updateLocalStorage(store.getState())} }
dan menghapus waktu memeriksa logika dalam.
Mengembangkan saran yang sangat baik dan kutipan kode pendek yang disediakan dalam jawaban lain (dan artikel Medium Jam Creencia ), inilah solusi lengkap!
Kami membutuhkan file yang berisi 2 fungsi yang menyimpan / memuat status ke / dari penyimpanan lokal:
// FILE: src/common/localStorage/localStorage.js
// Pass in Redux store's state to save it to the user's browser local storage
export const saveState = (state) =>
{
try
{
const serializedState = JSON.stringify(state);
localStorage.setItem('state', serializedState);
}
catch
{
// We'll just ignore write errors
}
};
// Loads the state and returns an object that can be provided as the
// preloadedState parameter of store.js's call to configureStore
export const loadState = () =>
{
try
{
const serializedState = localStorage.getItem('state');
if (serializedState === null)
{
return undefined;
}
return JSON.parse(serializedState);
}
catch (error)
{
return undefined;
}
};
Fungsi-fungsi itu diimpor oleh store.js tempat kami mengonfigurasi toko kami:
CATATAN: Anda harus menambahkan satu ketergantungan: npm install lodash.throttle
// FILE: src/app/redux/store.js
import { configureStore, applyMiddleware } from '@reduxjs/toolkit'
import throttle from 'lodash.throttle';
import rootReducer from "./rootReducer";
import middleware from './middleware';
import { saveState, loadState } from 'common/localStorage/localStorage';
// By providing a preloaded state (loaded from local storage), we can persist
// the state across the user's visits to the web app.
//
// READ: https://redux.js.org/recipes/configuring-your-store
const store = configureStore({
reducer: rootReducer,
middleware: middleware,
enhancer: applyMiddleware(...middleware),
preloadedState: loadState()
})
// We'll subscribe to state changes, saving the store's state to the browser's
// local storage. We'll throttle this to prevent excessive work.
store.subscribe(
throttle( () => saveState(store.getState()), 1000)
);
export default store;
Toko diimpor ke index.js sehingga dapat dilewatkan ke Penyedia yang membungkus App.js :
// FILE: src/index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './app/core/App'
import store from './app/redux/store';
// Provider makes the Redux store available to any nested components
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Perhatikan bahwa impor absolut memerlukan perubahan ini ke YourProjectFolder / jsconfig.json - ini memberi tahu tempat untuk mencari file jika tidak dapat menemukannya pada awalnya. Jika tidak, Anda akan melihat keluhan tentang upaya mengimpor sesuatu dari luar src .
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}