我想将状态树的某些部分持久保存到localStorage中。这样做合适的地方是什么?减速器或动作?
我想将状态树的某些部分持久保存到localStorage中。这样做合适的地方是什么?减速器或动作?
Answers:
减速器永远都不适合这样做,因为减速器应该是纯净的,没有副作用。
我建议仅在订户中执行此操作:
store.subscribe(() => {
// persist your state
})
在创建商店之前,请阅读以下保留的部分:
const persistedState = // ...
const store = createStore(reducer, persistedState)
如果您使用combineReducers()
它,则会注意到尚未接收到该状态的reducers将使用其默认state
参数值正常启动。这可能非常方便。
建议您对订户进行反跳操作,以免写到localStorage的速度过快,否则会出现性能问题。
最后,您可以创建一个中间件来封装该中间件作为替代方案,但是我将从订户入手,因为它是一个更简单的解决方案,并且做得很好。
要填补丹·阿布拉莫夫的答案的空白,您可以这样使用store.subscribe()
:
store.subscribe(()=>{
localStorage.setItem('reduxState', JSON.stringify(store.getState()))
})
在创建商店之前,请localStorage
像下面这样检查并解析密钥下的所有JSON:
const persistedState = localStorage.getItem('reduxState')
? JSON.parse(localStorage.getItem('reduxState'))
: {}
然后persistedState
,您可以将此常量传递给您的createStore
方法,如下所示:
const store = createStore(
reducer,
persistedState,
/* any middleware... */
)
persistedState
返回initialState
而不是一个空对象?否则,我认为createStore
将使用该空对象进行初始化。
如果上述解决方案有任何问题,您可以写信给自己。让我告诉你我做了什么。忽略saga middleware
事物只关注两个事物localStorageMiddleware
和reHydrateStore
方法。在localStorageMiddleware
拉所有redux state
,并把它放在local storage
和rehydrateStore
把所有的applicationState
本地存储(如果存在),并将其放在redux 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
即使商店中的任何内容都没有变化,这也不会导致大量写入吗?您如何补偿不必要的写入
我无法回答@Gardezi,但基于他的代码的选项可能是:
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
)
);
所不同的是,我们只保存了一些操作,您可以使用去抖功能来仅保存状态的最后一次互动
我有点迟了,但是我根据这里所述的示例实现了持久状态。如果您只想每隔X秒更新一次状态,则该方法可以为您提供帮助:
定义包装函数
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!")
}
}
在您的订户中调用包装函数
store.subscribe((state) =>
{
updateLocalStorage(store.getState())
});
在此示例中,无论触发更新的频率如何,状态最多每5秒更新一次。
(state) => { updateLocalStorage(store.getState()) }
在lodash.throttle
这样的:store.subscribe(throttle(() => {(state) => { updateLocalStorage(store.getState())} }
和删除的时间检查逻辑里面。
在其他答案(以及Jam Creencia的中篇文章)中提供的出色建议和简短代码摘录的基础上,这是一个完整的解决方案!
我们需要一个包含2个函数的文件,这些函数可以将状态保存到本地存储或从本地存储加载状态:
// 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;
}
};
这些功能是由store.js导入的,我们在其中配置了商店:
注意:您需要添加一个依赖项: 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;
商店被导入到index.js中,因此可以将其传递给包装App.js的Provider :
// 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')
)
请注意,绝对导入要求对YourProjectFolder / jsconfig.json进行此更改-如果它最初无法找到文件,则会告诉它在哪里寻找文件。否则,您会看到有关尝试从src外部导入内容的抱怨。
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}