📝
marcyannotes
  • Welcome
  • Student Guidelines & Policies
    • Student Handbook
    • AI Policy
    • Academic Calendar
  • Environment Setup
    • Local Environment Setup - Mac
    • Local Environment Setup - Windows
    • GitHub Setup
    • Postgres Setup
  • Fullstack Software Engineering Curriculum
    • Overview
    • How-Tos
      • How To Code at Marcy: Code Style Guide
      • How to Do Short Response and Coding Assignments
      • How to Debug
      • How to PEDAC
      • How to Create Projects with Vite
      • How to Deploy on GitHub Pages
      • How to Deploy on Render
    • Mod 0 - Command Line Interfaces, Git, and GitHub
      • Mod 0 Overview
      • Command Line Interfaces
      • Git & GitHub
      • Git Pulling & Merging
      • Git Branching & PRs
      • Pair Programming: BONUS
    • Mod 1 - JavaScriptFundamentals
      • Mod 1 Overview
      • Intro to Programming
      • Errors
      • Node & Node Modules
      • Variables, Functions & String Methods
      • Control Flow, typeof, and Math
      • Loops
      • Arrays
      • Objects
      • Higher Order Functions: Callbacks
      • Higher Order Functions: Array Methods
      • Regex
    • Mod 2 - HTML, CSS & the DOM
      • Mod 2 Overview
      • HTML
      • CSS
      • Accessibility (a11y)
      • The DOM
      • Events
      • Forms
      • The Box Model and Positioning
      • Flexbox
      • Grid & Media Queries
      • ESModules
      • LocalStorage
    • Mod 3 - Async & APIs
      • Mod 3 Overview
      • Promises
      • Fetch
      • Building a Fetching App
      • Async & Await
    • Mod 4 - Project Week!
      • Project Week Overview
    • Mod 5 - Object-Oriented Programming
      • Mod 5 Overview
      • Intro to OOP, Encapsulation, Factory Functions, and Closure
      • Classes
      • Private & Static
      • Has Many/Belongs To
      • Polymorphism
    • Mod 6 - Data Structures & Algorithms
      • Mod 6 Overview
      • Stacks & Queues
      • Nodes & Linked Lists
      • Singly & Doubly Linked Lists
      • Recursion
      • Trees
    • Mod 7 - React
      • Mod 7 Overview
      • Intro to React
      • Events, State, and Forms
      • Fetching with useEffect
      • Building a Flashcards App
      • React Context
      • Global Context Pattern
      • React Router
    • Mod 8 - Backend
      • Mod 8 Overview
      • Intro to Express
      • Building a Static Web Server with Middleware
      • Securing API Keys with Environment Variables
      • Building a RESTful API with MVC
      • SQL and Databases
      • JOIN (Association) SQL Queries
      • Knex
      • Your First Fullstack App!
      • Migrations & Seeds
      • Schema Design & Normalization
      • Hashing Passwords with Bcrypt
  • Code Challenge Curriculum
    • Unit 0
      • Lecture: Functions in JS
      • CC-00: Functions and Console Logs
      • CC-01: Conditionals
      • CC-02: Conditionals 2
    • Unit 1
      • CC-03: For Loops
      • CC-04: For Loops and Conditionals
      • CC-05: For Loops and Conditionals 2
    • Unit 2
      • CC-06: String Mutations
      • CC-07: Array Iteration
      • CC-08: String Mutation and Array Iteration
      • CC-09: Array Mutations
      • CC-10: Reading Objects
      • CC-11: Objects
      • CC-12: Objects
      • Unit 2 Diagnostic
    • Unit 3
      • Intro to PEDAC (and Algorithms)
      • validTime
      • fizzBuzz (array)
      • digitSumDifference
      • firstNotRepeating
      • compareEvenAndOddSum
      • countVowelConsonants
      • finalHP
      • canMakeTriangle
    • Unit 4
    • Unit 5
    • Unit 6
    • Unit 7
    • Unit 8
    • Sorting
Powered by GitBook
On this page
  • What is localStorage?
  • Setting and Getting Values
  • toString() vs. JSON.stringify()
  • Stringify and Parse
  • localStorage Helpers
  • Data Layer
  1. Fullstack Software Engineering Curriculum
  2. Mod 2 - HTML, CSS & the DOM

LocalStorage

PreviousESModulesNextMod 3 - Async & APIs

Last updated 8 months ago

Follow along with code examples !

Table of Contents

What is localStorage?

The localStorage interface allows you to store data across browser sessions.

It is an Object that is available on the window (it is globally available) and it has 4 methods:

  • localStorage.setItem(key, valueString)

  • localStorage.getItem(key)

  • localStorage.removeItem(key)

  • localStorage.clear()

Setting and Getting Values

Storing data in a user's localStorage is like storing data in an Object. Every value we store is associated with a key.

  • We store (set) values using localStorage.setItem(key, valueString)

  • We retrieve (get) values using localStorage.getItem(key)

Note: localStorage can only store strings which we have to watch out for.

localStorage.setItem('luckyNumber', 13);
localStorage.setItem('favoriteColor', 'purple');

const storedNumber = localStorage.getItem('luckyNumber');
const storedColor = localStorage.getItem('favoriteColor');

console.log(storedNumber);          // 13
console.log(storedColor);           // purple
console.log(typeof storedNumber);   // string
console.log(typeof storedColor);    // string

toString() vs. JSON.stringify()

When using setItem, the value is turned into a string before being stored. It does this using the given value's .toString() method (every data type has this method).

As you can see, this is particularly annoying when dealing with Arrays and Objects because of the way that they get turned into strings.

const num = 13;
const bool = true;
const str = 'purple';
const arr = [1, 2, 3];
const obj = { name: 'ben' };

console.log(num.toString());  // '13'
console.log(bool.toString()); // 'true'
console.log(str.toString());  // 'purple'
console.log(arr.toString());  // '1,2,3'  <-- mildly annoying that the [] are gone
console.log(obj.toString());  // '[object Object]'  <-- what is this garbage??

// JSON.stringify() is much better
console.log(JSON.stringify(arr)); // [1, 2, 3]
console.log(JSON.stringify(obj)); // { name: 'ben' }

However, the JSON.stringify() method preserves the structure of Arrays and Objects.

Stringify and Parse

Before storing values in localStorage, we should JSON.stringify() them.

const instructors = ['ben', 'gonzalo', 'motun', 'zo', 'carmen'];
const user = { name: 'ben', canCode: true };

// We typically will JSON.stringify() the value before we set it... 
localStorage.setItem('instructors', JSON.stringify(instructors));
localStorage.setItem('user', JSON.stringify(user));

// ...and JSON.parse() the value when we get it:
const storedInstructors = JSON.parse(localStorage.getItem('instructors'));
const storedUser = JSON.parse(localStorage.getItem('user'));

console.log('storedInstructors:', storedInstructors);
console.log('storedUser:', storedUser);

When retrieving a value from localStorage, we use the JSON.parse() method which takes a string and tries to turn it into a value

localStorage Helpers

That's quite a bit of code to write and re-write every time we can to set or get values to/from localStorage.

To reduce repetition, we often write these two helper functions:

const setLocalStorageKey = (key, value) => {
  localStorage.setItem(key, JSON.stringify(value))
}

const getLocalStorageKey = (key) => {
  try {
    return JSON.parse(localStorage.getItem(key))
  } catch (err) {
    console.error(err);
    return null;
  }
}

We wrap the JSON.parse() function invocation in a try/catch block in the event that JSON.parse() can't determine the value type of the given string.

  • If it can, it will return the value.

  • If it can't, the error will be printed (and not break everything) and null will be returned.

Now, we can safely use these functions instead of the localStorage ones and know that all values will be properly stored and retrieved.

setLocalStorageKey('nums', [1, 2, 3])
const storedArr = getLocalStorageKey('nums');

setLocalStorageKey('user', { name: 'ben' });
const storedUser = getLocalStorageKey('user');

console.log(storedArr);  // [1, 2, 3]
console.log(storedUser); // { name: 'ben' }

Data Layer

As you can see, working with localStorage can be quite tricky. We want to ensure that our application works in a consistent and predictable manner.

To achieve this, we will typically:

  • isolate the logic for dealing with localStorage in its own file.

  • create functions for interacting with localStorage.

  • only export the functions that indirectly interact with localStorage.

This way, we create a consistent and predictable interface.

// local-storage.js

// Generic localStorage helpers
const setLocalStorageKey = (key, value) => {
  localStorage.setItem(key, JSON.stringify(value))
}

const getLocalStorageKey = (key) => {
  try {
    return JSON.parse(localStorage.getItem(key))
  } catch (err) {
    console.error(err);
    return null;
  }
}

// These two will be used by the rest of the functions a lot
export const getNames = () => getLocalStorageKey('names');
export const setNames = (names) => setLocalStorageKey('names', names);

// More helper functions
export const initializeNames = () => setNames(['ben', 'gonzalo', 'motun']);

export const addName = (name) => {
  const names = getNames();
  setNames([...names, name]);
}

export const removeName = (nameToRemove) => {
  const names = getNames().filter((name) => name !== nameToRemove);
  setNames([...names]);
}

And always remember to test your code!

// mains.js
import { initializeNames, addName, getNames } from './local-storage.js';

initializeNames();
console.log(getNames());  // ['ben', 'gonzalo', 'motun']

addName('carmen');
addName('zo');
console.log(getNames());  // ['ben', 'gonzalo', 'motun', 'carmen', 'zo]

removeName('ben');
console.log(getNames());  // ['gonzalo', 'motun', 'carmen', 'zo]

Q: What makes this predictable and consistent?

Answer

This is predictable and consistent because

  • we control what the user of these functions can do (set, get, initialize, add, remove)

  • the caller of those exported functions doesn't directly interact with localStorage

  • the exported functions handle the interaction with localStorage

Sure, we can interact with localStorage outside of this file too but we should avoid that if we want to maintain the predictable and consistent behavior.

That file acts as a data layer. We might also decide to isolate our DOM manipulation code and create a DOM layer or create an event handling layer.

Using localStorage, we will build a data layer that is used to inform what is rendered, know as the view layer. When users interact with the view through the form, the data layer will be updated and we re-render the view.

This cycle of data > view > handle events > data looks like this:

here
What is localStorage?
Setting and Getting Values
toString() vs. JSON.stringify()
Stringify and Parse
localStorage Helpers
Data Layer