Ich möchte einige Teile meines Statusbaums im localStorage beibehalten. Was ist der geeignete Ort dafür? Reduzierer oder Aktion?
Ich möchte einige Teile meines Statusbaums im localStorage beibehalten. Was ist der geeignete Ort dafür? Reduzierer oder Aktion?
Antworten:
Reduktionsmittel sind niemals ein geeigneter Ort, um dies zu tun, da Reduzierstücke rein sein sollten und keine Nebenwirkungen haben sollten.
Ich würde empfehlen, es nur in einem Abonnenten zu tun:
store.subscribe(() => {
// persist your state
})
Lesen Sie vor dem Erstellen des Geschäfts die folgenden Teile:
const persistedState = // ...
const store = createStore(reducer, persistedState)
Wenn Sie verwenden, werden combineReducers()
Sie feststellen, dass Reduzierungen, die den Status nicht erhalten haben, wie gewohnt mit ihrem Standardargumentwert "hochfahren" state
. Das kann sehr praktisch sein.
Es ist ratsam, dass Sie Ihren Abonnenten entprellen, damit Sie nicht zu schnell in localStorage schreiben, da sonst Leistungsprobleme auftreten.
Schließlich können Sie eine Middleware erstellen, die dies als Alternative kapselt. Ich würde jedoch mit einem Abonnenten beginnen, da dies eine einfachere Lösung ist und die Aufgabe gut erledigt.
Um die Lücken in Dan Abramovs Antwort zu füllen, könnten Sie Folgendes verwenden store.subscribe()
:
store.subscribe(()=>{
localStorage.setItem('reduxState', JSON.stringify(store.getState()))
})
Überprüfen localStorage
und analysieren Sie vor dem Erstellen des Speichers JSON unter Ihrem Schlüssel wie folgt:
const persistedState = localStorage.getItem('reduxState')
? JSON.parse(localStorage.getItem('reduxState'))
: {}
Sie übergeben diese persistedState
Konstante dann createStore
wie folgt an Ihre Methode:
const store = createStore(
reducer,
persistedState,
/* any middleware... */
)
persistedState
zurückgeben initialState
statt einem leeren Objekt? Ansonsten denke ich, createStore
wird mit diesem leeren Objekt initialisiert.
Mit einem Wort: Middleware.
Schauen Sie sich Redux-Persist an . Oder schreibe deine eigenen.
[UPDATE 18 Dec 2016] Bearbeitet, um die Erwähnung von zwei ähnlichen Projekten zu entfernen, die jetzt inaktiv oder veraltet sind.
Wenn jemand Probleme mit den oben genannten Lösungen hat, können Sie Ihre eigenen schreiben. Lassen Sie mich Ihnen zeigen, was ich getan habe. Ignorieren Sie saga middleware
Dinge, konzentrieren Sie sich nur auf zwei Dinge localStorageMiddleware
und reHydrateStore
Methoden. Das localStorageMiddleware
zieht alles redux state
und legt es ein local storage
und rehydrateStore
zieht alles applicationState
in den lokalen Speicher, falls vorhanden und legt es einredux 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
selbst wenn sich nichts im Laden geändert hat? Wie Sie unnötige Schreibvorgänge kompensieren
Ich kann @Gardezi nicht beantworten, aber eine Option, die auf seinem Code basiert, könnte sein:
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
)
);
Der Unterschied besteht darin, dass wir nur einige Aktionen speichern. Sie können eine Entprellungsfunktion verwenden, um nur die letzte Interaktion Ihres Status zu speichern
Ich bin etwas spät dran, habe aber einen dauerhaften Zustand gemäß den hier angegebenen Beispielen implementiert. Wenn Sie den Status nur alle X Sekunden aktualisieren möchten, kann dieser Ansatz Ihnen helfen:
Definieren Sie eine Wrapper-Funktion
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!")
}
}
Rufen Sie eine Wrapper-Funktion in Ihrem Abonnenten auf
store.subscribe((state) =>
{
updateLocalStorage(store.getState())
});
In diesem Beispiel wird der Status höchstens alle 5 Sekunden aktualisiert , unabhängig davon, wie oft eine Aktualisierung ausgelöst wird.
(state) => { updateLocalStorage(store.getState()) }
in lodash.throttle
etwa so aus : store.subscribe(throttle(() => {(state) => { updateLocalStorage(store.getState())} }
und entfernen Zeitlogik innerhalb überprüfen.
Aufbauend auf den hervorragenden Vorschlägen und Kurzcode- Auszügen, die in anderen Antworten (und in Jam Creencias Medium-Artikel ) enthalten sind, finden Sie hier eine vollständige Lösung!
Wir benötigen eine Datei mit 2 Funktionen, die den Status im / vom lokalen Speicher speichern / laden:
// 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;
}
};
Diese Funktionen werden von store.js importiert, in dem wir unseren Store konfigurieren:
HINWEIS: Sie müssen eine Abhängigkeit hinzufügen: 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;
Der Store wird in index.js importiert, damit er an den Provider übergeben werden kann, der App.js umschließt :
// 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')
)
Beachten Sie, dass für absolute Importe diese Änderung in YourProjectFolder / jsconfig.json erforderlich ist. Hier erfahren Sie, wo nach Dateien gesucht werden muss, wenn sie zuerst nicht gefunden werden können. Andernfalls werden Beschwerden über den Versuch angezeigt, etwas von außerhalb von src zu importieren .
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}