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

IT技术 javascript redux local-storage state
2021-02-04 11:32:15

我想将状态树的某些部分保留到 localStorage。什么地方适合这样做?减速器还是动作?

6个回答

Reducer 永远不是一个合适的地方,因为 reducer 应该是纯粹的并且没有副作用。

我建议只在订阅者中进行:

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

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

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

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

建议您对订阅者进行去抖,以免写入 localStorage 太快,否则会出现性能问题。

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

如果我只想订阅状态的一部分怎么办?那可能吗?我继续做了一些有点不同的事情:1. 在 redux 之外保存持久状态。2. 使用 redux 操作在 react 构造函数(或使用 componentWillMount)中加载持久状态。2 完全没问题,而不是直接在商店中加载持久化数据?(我试图为 SSR 单独保留)。顺便感谢 Redux!太棒了,我喜欢它,在迷失了我的大项目代码之后,现在一切都恢复了简单和可预测的 :)
2021-03-14 11:32:15
Dan Abramov 还制作了一个完整的视频:egghead.io/lessons/...
2021-03-15 11:32:15
这看起来可能很浪费。OP 确实说只有一部分状态需要被持久化。假设您有一家拥有 100 个不同密钥的商店。您只想保留其中不常更改的 3 个。现在,您正在解析和更新本地存储,对 100 个键中的任何一个进行微小更改,同时您有兴趣保留的 3 个键甚至都没有更改过。下面@Gardezi 的解决方案是一种更好的方法,因为您可以将事件侦听器添加到中间件中,以便您在实际需要时进行更新,例如:codepen.io/retrcult/pen/qNRzKN
2021-03-19 11:32:15
在每次商店更新时序列化状态是否存在合理的性能问题?浏览器是否在单独的线程上序列化?
2021-03-20 11:32:15
我认为所有这些答案都有些误导,谁说他们想在页面加载时执行 localStorage,如果我们想与操作或效果一起执行该怎么办。订阅有什么作用?每次都打电话?
2021-04-06 11:32:15

要填写 Dan Abramov 的答案的空白,您可以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... */
)
如果 localStorage 中没有任何内容,是否应该persistedState返回initialState而不是空对象?否则我认为createStore会用那个空对象初始化。
2021-03-23 11:32:15
@Alex 你是对的,除非你的 initialState 是空的。
2021-03-23 11:32:15
简单有效,没有额外的依赖。
2021-04-07 11:32:15
使用 combineReducers 时是否有一种很好的方法可以做到这一点,而您只想保留一个存储?
2021-04-08 11:32:15
创建一个中间件可能看起来好一点,但我同意它是有效的,并且对于大多数情况来说已经足够了
2021-04-11 11:32:15

总之一句话:中间件。

查看redux-persist或者自己写。

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

如果有人对上述解决方案有任何问题,您可以自己写。让我告诉你我做了什么。忽略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;
嗯,它有效,反正有这个好主意吗?问题:如果我们有更多数据,即多个具有大数据的减速器,会发生什么?
2021-03-21 11:32:15
嗨,localStorage即使商店中没有任何变化,这不会导致大量写入吗?您如何补偿不必要的写入
2021-03-27 11:32:15

我无法回答@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
    )
);

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