How to Make LocalStorage Reactive in Vue

How to Make LocalStorage Reactive in Vue

css-tricks.com css-tricks.com2 weeks ago in #Dev Love66

Reactivity is one of Vue’s greatest features. It is also one of the most mysterious if you don’t know what it’s doing behind the scenes. Like, why does it work with objects and arrays and not with other things, like localStorage? Let’s answer that that question, and while we’re at it, make Vue reactivity work with localStorage. If we were to run the following code, we would see that the counter being displayed as a static value and not change like we might expect it to because of the interval changing the value in localStorage. new Vue({   el: “#counter”,   data: () =({     counter: localStorage.getItem(“counter”)   }),   computed: {     even() {       return this.counter % 2 == 0;     }   },   template: `     Counter: {{ counter }}     Counter is {{ even ? ‘even’ : ‘odd’ }}   ` }); // some-other-file.js setInterval(() ={   const counter = localStorage.getItem(“counter”);   localStorage.setItem(“counter”, counter 1); }, 1000); While the counter property inside the Vue instance is reactive, it won’t change just because we changed its origin in localStorage.  There are multiple solutions for this, the nicest perhaps is using Vuex and keep the store value in sync with localStorage. But what if we need something simple like what we have in this example? We have to take a dive in how Vue’s reactivity system works. Reactivity in Vue When Vue initializes a component instance, it observes the data option. This means it walks through all of the properties in data and converts them to getters/setters using Object.defineProperty. By having a custom setter for each property, Vue knows when a property changes, and it can notify the dependents that need to react to the change. How does it know which dependents rely on a property? By tapping into the getters, it can register when a computed property, watcher function, or  render function accesses a data prop. // core/instance/state.js function initData () {   // …   observe(data) } // core/observer/index.js export function observe (value) {   // …   new Observer(value)   // … } export class Observer {   // …   constructor (value) {     // …     this.walk(value)   }      walk (obj) {     const keys = Object.keys(obj)     for (let i = 0; i < keys.length; i ) {       defineReactive(obj, keys[i])     }   } }  
 export function defineReactive (obj, key, ...) {   const dep = new Dep()   // ...   Object.defineProperty(obj, key, {     // ...     get() {       // ...       dep.depend()       // ...     },     set(newVal) {       // ...       dep.notify()     }   }) } So, why isn’t localStorage reactive? Because it’s not an object with properties. But wait. We can’t define getters and setters with arrays either, yet arrays in Vue are still reactive. That’s because arrays are a special case in Vue. In order to have reactive arrays, Vue overrides array methods behind the scenes and patches them together with Vue’s reactivity system. Could we do something similar with localStorage? Overriding localStorage functions As a first try, we can fix our initial example by overriding localStorage methods to keep track which component instances requested a localStorage item. // A map between localStorage item keys and a list of Vue instances that depend on it const storeItemSubscribers = {}; 
 const getItem = window.localStorage.getItem; localStorage.getItem = (key, target)...

Like to keep reading?

This article first appeared on css-tricks.com. If you'd like to keep reading, follow the white rabbit.

View Full Article

Leave a Reply