乐闻世界logo
搜索文章和话题

Redux 中什么时候执行本地存储 localstorage?

7 个月前提问
3 个月前修改
浏览次数131

6个答案

1
2
3
4
5
6

在Redux中,将状态写入localStorage通常是为了持久化某些数据,以便在页面刷新或关闭后再次访问时能够恢复这些数据。选择何时将状态写入localStorage通常取决于应用的具体需求,但以下是一些合适的时机:

  1. 应用退出或页面关闭之前: 可以监听window上的beforeunload事件,在应用即将关闭前将当前状态保存到localStorage。这样做可以确保即使用户没有显式地保存他们的进度,他们的数据也不会丢失。

  2. 状态更新时: 如果希望应用的状态实时同步到localStorage,可以在Redux的中间件中做这件事情。例如,你可以使用redux-thunkredux-saga中间件在每次有相关action被分发且状态更新后,将新状态保存到localStorage。

  3. 定期自动保存: 对于一些需要自动保存功能的应用,比如在线编辑器,可以设置一个定时器,每隔一定时间将当前状态保存到localStorage

  4. 特定的action被分发时: 可以在分发某些特定action,如SAVE_DATA,时将状态或状态的一部分保存到localStorage

举个例子,假设你有一个在线待办事项应用,用户可以添加、编辑和删除待办事项。你可能希望在以下几种情况下将待办事项列表保存到localStorage

  • 用户新增、修改或删除待办事项后,确保这些更改即时保存。
  • 用户关闭浏览器标签或窗口前,保证待办事项的最新状态被保留下来。
  • 用户可以点击一个“保存”按钮显式地将待办事项列表保存到localStorage

以下是一个简单的中间件示例,它会在每次action分发后同步状态到localStorage

javascript
const localStorageMiddleware = store => next => action => { const result = next(action); localStorage.setItem('my-app-state', JSON.stringify(store.getState())); return result; };

然而,这里有一点很重要:不是所有的状态都应该存储在localStorage中,因为localStorage有容量限制,通常为5MB。因此,只有那些必要的、体积较小的状态才应该被持久化。同时,还需要考虑安全性因素,敏感信息绝不应该存储在localStorage中。

2024年6月29日 12:07 回复

减速器从来都不是执行此操作的合适位置,因为减速器应该是纯粹的并且没有副作用。

我建议只在订阅者中执行此操作:

shell
store.subscribe(() => { // persist your state })

在创建存储之前,请阅读这些持久部分:

shell
const persistedState = // ... const store = createStore(reducer, persistedState)

如果您使用,combineReducers()您会注意到尚未收到状态的减速器将使用其默认state参数值正常“启动”。这非常方便。

建议您取消订阅者的抖动,这样就不会太快地写入 localStorage,否则会出现性能问题。

最后,您可以创建一个封装它的中间件作为替代方案,但我会从订阅者开始,因为它是一个更简单的解决方案并且可以很好地完成工作。

2024年6月29日 12:07 回复

要填补 Dan Abramov 答案的空白,您可以store.subscribe()这样使用:

shell
store.subscribe(()=>{ localStorage.setItem('reduxState', JSON.stringify(store.getState())) })

在创建存储之前,请检查localStorage并解析密钥下的任何 JSON,如下所示:

shell
const persistedState = localStorage.getItem('reduxState') ? JSON.parse(localStorage.getItem('reduxState')) : {}

然后,您可以将此常量传递persistedState给您的createStore方法,如下所示:

shell
const store = createStore( reducer, persistedState, /* any middleware... */ )
2024年6月29日 12:07 回复

简而言之:使用中间件redux-persist,或者自己写。

2024年6月29日 12:07 回复

如果有人对上述解决方案有任何疑问,您可以写下自己的解决方案。让我告诉你我做了什么。忽略saga middleware事物,只关注两件事localStorageMiddlewarereHydrateStore方法。拉出localStorageMiddleware所有内容并将redux state其放入本地存储中(如果存在)并将其放入local storage``rehydrateStore``applicationState``redux store

shell
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;
2024年6月29日 12:07 回复

基于其他答案(以及Jam Creencia 的 Medium 文章)中提供的出色建议和简短代码摘录,这是一个完整的解决方案!

我们需要一个包含 2 个函数的文件,用于将状态保存到本地存储或从本地存储加载状态:

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

运行代码片段Hide results

展开片段

这些函数由store.js导入,我们在其中配置商店:

注意:您需要添加一个依赖项:npm install lodash.throttle

shell
// 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;

运行代码片段Hide results

展开片段

该商店被导入到index.js中,因此可以将其传递到包装****App.js的Provider中:

shell
// 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') )

运行代码片段Hide results

展开片段

请注意,绝对导入需要对YourProjectFolder/jsconfig.json进行此更改- 这告诉它在第一次找不到文件时在哪里查找文件。否则,您会看到有关尝试从src外部导入某些内容的抱怨。

shell
{ "compilerOptions": { "baseUrl": "src" }, "include": ["src"] }

运行代码片段Hide results

展开片段

2024年6月29日 12:07 回复

你的答案