diceline-chartmagnifierquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

Making only some state properties persistent inside a persistent Vuex module

Making data persistent in web applications is a common required feature. State management patterns and libraries like Vuex provide systems for storing data in the local storage for achieving the persistence of data, making the data survive over refresh and making it reusable without having to request the data again.

The local storage has a (configurable) memory limit. Overflowing can cause unwanted behaviour and make the app work unexpectedly. Vuex gives the opportunity to make only some of its modules persistent, which is great.

Step 1: We import the modules and group them together in our modules object:

import store as module1 from '../module1Location'
import store as module2 from '../module2Location'
import store as module3 from '../module3Location'

const modules = {
  module1,
  module2,
  module3,
};

Step 2: For choosing which modules should be persistent, we can use the vuex-persistedstate plugin. We have to define the plugins array that will be passed to the createStore method like this:

const plugins = [
  createPersistedState({
    storage: window.localStorage,
    key: 'yourkey',
    paths: [
      'module1',
      'module2',
      'module3',
    ],
  }),
];

Step 3: Having only some modules to be persistent is nice. But what if we need to only make some parts of a module persistent? What if one of our modules should have all its state properties persistent except for one, the one that causes the unwanted local storage overflow?

Lets pretend our module3 has a property named bigData that causes the overlow.

Solution: we can add the REDUCER FUNCTION to our payload for the createPersistedState() function. Here is how our plugins array should look like:

const plugins = [
  createPersistedState({
    reducer: (state: RootState) => {
      const { module3, ...restOfRootState } = state;
      const { bigData, ...restOfModule3State } = module3;
      return {
        ...restOfRootState,
        module3: { ...restOfModule3State },
      };
    },
    storage: window.localStorage,
    key: 'yourkey',
    paths: [
      'module1',
      'module2',
      'module3',
    ],
  }),
];

Explanation of the reducer function: The reducer function takes in a state and returns the new state that should be persisted. We can change the state that we receive initially to only contain the data that we wish. Lets see how we extract the problematic bigData out of our module.

  1. We get our RootState as a parameter. This is an object containing all our Vuex modules.
  2. We extract in a variable our problematic module3 out of this RootState object and keep all other (non-problematic) modules in another separate object using the rest operator.
  3. We extract our problematic bigData from our module3 variable (the one extracted at step2) and keep all other (non-problematic) state properties in another object using the rest operator.
  4. We return the new state object that we build by spreading out the object containing the non-problematic modules using the spread operator and by redefining our module3 that should now contain only the non-problematic state properties (all its previous properties except bigData).

Step 4: passing the modules and plugins objects to the createStore() function, through its payload.

export const store = createStore({
  modules,
  plugins,
});

After this, the local storage will only help persist our modules, without the module3's bigData object that was causing our local storage overflow.