The e-commerce site now works great! We can add a product, list the products, open the product details page, and also add a product to the shopping cart.

But when we reload the page, all the data goes away!

This is because we don’t have any way of persisting the data. It’s all volatile.

There are various ways to persist data. The simplest one is to use the Web Storage API provided by browsers and use either localStorage or sessionStorage (there are differences in how they work).

We choose to use localStorage because it’s more persistent than sessionStorage. Without going too fancy, we simply go and add calls to window.localStorage every time we update our state.

We handle all the state in the App component, in the functions

  • addProduct
  • deleteProduct
  • addToCart

At the end of each of those functions we call localStorage.setItem() with the corresponding data:

const addProduct = product => {
  const updatedProducts = [...products, product]
  setProducts(updatedProducts)
  localStorage.setItem('products', JSON.stringify(updatedProducts))
}

const deleteProduct = index => {
  let updatedProducts = [...products]
  updatedProducts = updatedProducts
    .slice(0, index)
    .concat(updatedProducts.slice(index + 1, updatedProducts.length))
  setProducts(updatedProducts)
  localStorage.setItem('products', JSON.stringify(updatedProducts))
}

const addToCart = ({ product, quantity }) => {
  const index = cart.findIndex(
    itemInCart => itemInCart.product.slug === product.slug
  )

  let newCart = []

  if (index === -1) {
    //not existing
    newCart = [...cart, { product, quantity }]
  } else {
    quantity += cart[index].quantity
    newCart = cart
      .filter(item => item.product.slug !== product.slug)
      .concat({ product, quantity })
  }
  setCart(newCart)
  localStorage.setItem('cart', JSON.stringify(newCart))
}

Then at startup time we need to update the state with the state we get from localStorage. We import useEffect() to do so, from react:

import React, { useState, useEffect } from 'react'

then we call it passing a second argument, [], which means our callback function (passed as first argument) is only called on mount, and not on update. See my hooks blog post for more info on useEffect.

useEffect(() => {
  setProducts(JSON.parse(localStorage.getItem('products')) || [])
  setCart(JSON.parse(localStorage.getItem('cart')) || [])
}, [])

You can go fancier with libraries that can do this for you or other complex code, but I like to keep things as simple as possible. Complex code is always going to hurt.

That’s it! Now the products and the cart works fine, and if we reload the page, the data persists!

The final code of the project is available at https://github.com/flaviocopes/react-ecommerce


Go to the next lesson