如何更新 LocalStorage 中持久化的 Redux 状态结构

Posted

技术标签:

【中文标题】如何更新 LocalStorage 中持久化的 Redux 状态结构【英文标题】:How To Update Structure of Redux State Persisted In LocalStorage 【发布时间】:2019-05-12 17:13:36 【问题描述】:

我将一部分 Redux 状态保存在 localStorage 中,以便在页面刷新时保持它。

如果我的 Redux 状态结构发生变化,如何更新 localStorage 中的状态结构?

示例:

这是我的 combineReducer 文件:

// combineReducer

const rootReducer = combineReducers(
  usersPage: usersPageReducer,
  form: formReducer
)

我的 usersPageReducer 中的原始默认状态如下所示:

// usersPageReducer.js

defaultUsersPageState = 
  selectedColumns: ["name", "email"],    

我使用以下方法加载并保存到 localStorage:

// localStorage.js

export const loadState = () => 
  try 
    const serializedState = localStorage.getItem('state')
    if (serializedState === null) 
      return undefined;
    
    return JSON.parse(serializedState)
   catch (err) 
    return undefined
  


export const saveState = (state) => 
  try 
    const serializedState = JSON.stringify(state)
    localStorage.setItem('state', serializedState)
   catch (err) 
    // to define
  

在我的主文件中,我订阅了商店,并保存了状态的usersPage 切片。我使用来自 localStorage 的初始状态创建商店:

// main.js
const persistedState = loadState();
const initialState =  ...persistedState ;

const store = createStore(
  rootReducer,
  initialState,
);

store.subscribe(() => 
  saveState(
    usersPage: store.getState().usersPage
  )
)

现在假设我想更改我的 usersPageReducer 默认状态,以便它也跟踪排序。

// usersPageReducer.js

defaultUsersPageState = 
  selectedColumns: ["name", "email"],
  sorting: "name": "asc"   

上述方法不起作用,因为在应用程序启动时,我们从 localStorage 中对 redux 初始状态进行了水合。旧的初始状态击败了 usersPageReducer defaultUsersPageState

由于 localStorage 不知道 usersPageReducersorting 键,并且我们从不使用默认状态,因此 sorting 键永远不会添加到 redux 状态。

有没有办法对 redux 存储进行水合,以便它使用默认状态初始化每个 reducer(好像我们没有使用 localStorage),然后传播存储的状态?

【问题讨论】:

您可以通过将默认状态与缓存状态深度合并来创建初始状态。这样做会将新值添加到旧的缓存状态。 我将在哪里进行合并?我是否必须将每个减速器的默认状态导入我的main.js 文件?在这种情况下,将defaultUsersPageState 导入main.js?这似乎无法在我的大型应用程序中扩展。 【参考方案1】:

两个可能的解决方案我可以建议你:-

1. 使用变量来跟踪应用程序的版本(更好的是 redux-version )。现在,每当更改此变量的值时,您的应用程序将清除用户的 LocalStorage 并使用 defaultUsersPageState 提供的全新的 redux 结构(或其子部分),而不是而不是从中加载可用的(陈旧的)数据。

你可以通过多种方式实现这一点,我的是

// localStorage.js

export const loadState = (CurrVersion) => 
  try 
    // check if version matches or not ... 
    // for first time localStorage.getItem('appVersion') will be null
    if (CurrVersion !== localStorage.getItem('appVersion'))
      localStorage.removeItem('state') // 
       // update the appVersion in LS with current version
      localStorage.setItem('appVersion', CurrVersion) 
      return undefined;
    
    else
      const serializedState = localStorage.getItem('state')
      if (serializedState === null) 
         return undefined;
       
       return JSON.parse(serializedState)
      
    catch (err) 
      return undefined
  

main.js

// change it whenever breaking changes are added and it makes sense 
const CurrVersion = 'MY-APP-VERSION-2.0' 
const persistedState = loadState(CurrVersion);
const initialState =  ...persistedState ;

const store = createStore(
  rootReducer,
  initialState,
);

store.subscribe(() => 
  saveState(
    usersPage: store.getState().usersPage
  )
)

    明确检查持久化数据的结构是否更改。如果更改,您可以删除以前的对象并使用已更新结构的默认初始状态。但请记住,此检查需要递归地(或深度地)完成,您可能会发现 lodash 对此很有用。

P.S:如果你有大项目,请使用为这个问题构建的 npm 包,例如 react-persist

【讨论】:

以上是关于如何更新 LocalStorage 中持久化的 Redux 状态结构的主要内容,如果未能解决你的问题,请参考以下文章

Next.js 具有 React 钩子和 localStorage 的持久状态

什么更持久,LocalStorage 或 PHP 会话?

如何将 localStorage 与 apollo-client 和 reactjs 一起使用?

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

Gatsbyjs & localStorage:持久化状态

iOS 7 webview 和 localStorage 持久化