在Redux应用程序中写入localStorage的位置?

Mar*_*ngh 150 javascript state local-storage redux

我想将状态树的某些部分保存到localStorage.适当的地方是什么?减速器还是动作?

Dan*_*mov 201

减速剂从来不是一个合适的地方,因为减速剂应该是纯净的,没有副作用.

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

store.subscribe(() => {
  // persist your state
})
Run Code Online (Sandbox Code Playgroud)

在创建商店之前,请阅读这些持久化部分:

const persistedState = // ...
const store = createStore(reducer, persistedState)
Run Code Online (Sandbox Code Playgroud)

如果您使用,combineReducers()您会注意到没有收到状态的Reducer将使用其默认state参数值正常"启动" .这可能非常方便.

建议你去掉你的用户,这样你就不会太快写入localStorage,或者你会遇到性能问题.

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

  • Dan Abramov还制作了一个完整的视频:https://egghead.io/lessons/javascript-redux-persisting-the-state-to-the-local-storage (33认同)
  • 这看起来很浪费。OP确实表示只需要保留一部分状态。假设您的商店有100个不同的钥匙。您只想保留其中三个不经常更改的对象。现在,您正在解析和更新本地存储,其中包括对100个键中的任何一个的微小更改,而与此同时,您对持久化感兴趣的3个键中的任何一个都没有改变过。以下@Gardezi的解决方案是一种更好的方法,因为您可以将事件侦听器添加到中间件中,以便仅在实际需要时进行更新,例如:https://codepen.io/retrcult/pen/qNRzKN (4认同)
  • 如果我只想订阅状态的一部分怎么办?那可能吗?我继续做一些有点不同的事情:1. 在 redux 之外保存持久状态。2. 使用 redux 操作在 react 构造函数(或使用 componentWillMount)中加载持久状态。2 完全没问题,而不是直接在商店中加载持久化数据?(我试图为 SSR 单独保留)。顺便感谢 Redux!太棒了,我喜欢它,在迷失了我的大项目代码之后,现在一切又回到了简单和可预测的状态:) (2认同)
  • 在每次商店更新时序列化状态是否存在合理的性能问题?浏览器是否可以在单独的线程上序列化? (2认同)

And*_*sen 141

要填写丹阿布拉莫夫答案的空白,你可以这样使用store.subscribe():

store.subscribe(()=>{
  localStorage.setItem('reduxState', JSON.stringify(store.getState()))
})
Run Code Online (Sandbox Code Playgroud)

在创建商店之前,检查localStorage并解析您的密钥下的任何JSON,如下所示:

const persistedState = localStorage.getItem('reduxState') ? JSON.parse(localStorage.getItem('reduxState')) : {}
Run Code Online (Sandbox Code Playgroud)

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

const store = createStore(
  reducer, 
  persistedState,
  /* any middleware... */
)
Run Code Online (Sandbox Code Playgroud)

  • 简单有效,没有额外的依赖性. (6认同)

Dav*_*lsh 44

总之一句:中间件.

查看redux-persist.或者自己写.

[更新2016年12月18日]已编辑删除提及现已停用或已弃用的两个类似项目.


Gar*_*ezi 8

如果有人对上述解决方案有任何疑问,您可以自己编写.让我告诉你我做了什么.忽略saga middleware事情只关注两件事localStorageMiddlewarereHydrateStore方法.在localStorageMiddleware拉所有redux state,并把它local storagerehydrateStore把所有的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;
Run Code Online (Sandbox Code Playgroud)

  • 嗨,即使商店中的任何内容都没有发生变化,这也不会导致对`localStorage`的大量写入吗?您如何补偿不必要的写入 (2认同)

Can*_*ram 6

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

我们需要一个包含 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;
  }
};
Run Code Online (Sandbox Code Playgroud)

这些函数由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;
Run Code Online (Sandbox Code Playgroud)

该商店被导入到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')
)
Run Code Online (Sandbox Code Playgroud)

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

{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}
Run Code Online (Sandbox Code Playgroud)


Dou*_*ina 5

我无法回答@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
    )
);
Run Code Online (Sandbox Code Playgroud)

不同之处在于我们只是保存一些动作,您可以使用 debounce 函数仅保存您状态的最后一次交互