Using local storage with React and Redux

Usually our application starts Redux's store with an empty object to show that the application doesn’t have any data when it starts. In the future when users have some interaction or when the application gets some data from the back-end so the store will keep these data. It is the most commom way to use Redux in a React project, at least in the projects which I worked til now, but sometimes we need another approach. To create a store with Redux we have to use a function called createStore which receives our reducers as parameters and should be imported from Redux.

Ok, first we can use as an example a reducer for a TODO application. Probably we have a file called reducers.js to create it.

export const todos = (state = [], action) => {
  switch (action.type) {
    case ADD_TODO:
      // add new todo
    case REMOV_TODO:
      // remove todos
    default:
      state
  }
}

With this reducer we can create a store. To do it we have to import the createStore function from Redux and our reducers in the file which we are creating the store, which probably is called app.js or something similar.

import reducers from './reducers'
import { createStore } from 'redux'

export const store = createStore(reducers)

Now we have our store with a TODO list which was initialized with empty array ([]).

Ok, it works pretty well. But in this case every time that our application is reloaded we lose all TODOs which we registered. If we have to keep they we have to save these TODOs in some database or something similar. Let’s think a little bit more and say that we have a more complex application which get some data from the back-end and they are not changed soon and we want to keep them when we reload the page. How can we do it? One solution to this situation is use the local storage which is a small “database” inside our browser.

Let’s take a look how todo that in our TODO app.

First we have to create two functions to get data from the local storage and to save data there. For it we can create a file called localStorage.js where we will have these functions.

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) {
    // ignore
  }
}

To use the local storage is pretty simple, it works as a list of key-values. When we want get data we call the function getItem from the global object called localStorage passing the key as a parameter. And to save data we call the function setItem from the same global object passing the key and the data. The data in local storage have to be always serialized and because of that when we load the data we have to deserialize using JSON.parse and when we send data we have to serialize they with JSON.stringify. Another point is that sometimes the local storage hasn’t permission to be accessed or for other reason we can get an error. Because of that, we have to use a try/catch when call functions from local storage.

Now we have to use our functions to save and load data from the local storage to our application. In our app.js file we will import our localStorage.js file and use its functions.

import { saveState, loadState } from './localStorage'

export const store = createStore(reducers, loadState())

store.subscribe(() => {
  saveState({
    todos: store.getState().todos
  })
})

Now in the function createStore we are passing a second parameter which is the return of the function loadSotre that we created to get the data from local storage. The function createStore from Redux accepts to receive data to hydrate the store. Take a look in the documentation. To save the data in the local storage we are using a function called subscribe from the store, which will be called every time when store is modified. For this function we are passing a function which call the saveState function which we created before and it will receive a new state of todos to be save in local storage.

With this new implementation we can save data in the local storage and reuse data without hit on the back-end every time that we reload our app and still use til we close the browser or browser’s tab.

If you want test this approach, please, take a look in this repository.

Written on November 29, 2016