I save a part of my Redux state in localStorage so that I can persist it across page refreshes.
How do I update the structure of the state in my localStorage if my Redux state structure changes?
Example:
This is my combineReducer file:
// combineReducer
const rootReducer = combineReducers({
  usersPage: usersPageReducer,
  form: formReducer
})
The original default state in my usersPageReducer looks like:
// usersPageReducer.js
defaultUsersPageState = {
  selectedColumns: ["name", "email"],    
}
I load and save to localStorage using the following methods:
// 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
  }
}
In my main file, I subscribe to the store, and save the usersPage slice of the state. And I create the store using initial state from localStorage:
// main.js
const persistedState = loadState();
const initialState = { ...persistedState };
const store = createStore(
  rootReducer,
  initialState,
);
store.subscribe(() => {
  saveState({
    usersPage: store.getState().usersPage
  })
})
Now imagine I want to change my usersPageReducer default state so that it also keeps track of sorting.
// usersPageReducer.js
defaultUsersPageState = {
  selectedColumns: ["name", "email"],
  sorting: {"name": "asc"}   
}
The above won't work because at the start of the app, we hydrate the redux initial state from localStorage. The old initial state beats out the usersPageReducer defaultUsersPageState. 
Since localStorage doesn't know about the sorting key for usersPageReducer, and we never use the default state, the sorting key is never added to the redux state.
Is there a way to hydrate the redux store so that it initializes every reducer using it's default state (as if we weren't using localStorage), then spreads the stored state?
There are two possible solution I can suggest you:-
1. Use a variable to keep track of the version of your application (even better redux-version ). Now whenever value of this variable is changed, your application will clear-out the user's LocalStorage and will use the brand new structure of your redux ( or its sub-part ) provided by defaultUsersPageState rather than loading the available ( stale ) data from  it.
You can achieve this in multiple ways, mine is
// 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
  }
}
And in 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
  })
})
P.S : Use npm packages built for this problem e.g react-persist if you have big projects .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With