Sunday, 29 August 2021

Does "export default new" produce a new instance of an object or singleton?

Original question:

vuex shared state in chrome extension

I have the following setup in a chrome extension;

  1. A content script that needs to write to a vuex store
  2. A background script that initializes that store
  3. And a popup script that renders stuff from the store (received from the content script)

store.js

import Vue from "vue";
import Vuex from "vuex";
import "es6-promise/auto";

import createMutationsSharer from "vuex-shared-mutations";

import dummyData from "./dummyData";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    chromePagesState: {
      allSections: [],
    },
  },
  mutations: {
    setChromePagesState(state, value) {
      state.chromePagesState = value;
    },
    addWhiteListedItem(state, item) {
      state.chromePagesState.allSections[0].itemSectionCategory[0].tasks.splice(
        0,
        0,
        item
      );
    },
  },
  actions: {
    // init from local storage
    async loadChromePagesState({ commit }) {
      const json = await getStorageValue("inventoryData");
      commit(
        "setChromePagesState",
        Object.keys(json).length === 0 && json.constructor === Object
          ? dummyData
          : JSON.parse(JSON.stringify(json))
      );
    },
    // send message to background script to call init (shortened)
    async loadChromePagesStateBrowser({ commit }) {
      browser.runtime
        .sendMessage({ type: "storeinit", key: "chromePagesState" })
        .then(async (chromePagesState) => {
          const json = await getStorageValue("inventoryData");
          commit(
            "setChromePagesState",
            Object.keys(json).length === 0 && json.constructor === Object
              ? dummyData
              : JSON.parse(JSON.stringify(json))
          );
        });
    },
  },
  // stuff from vuex-shared-mutations
  plugins: [
    createMutationsSharer({
      predicate: [
        "addWhiteListedItem",
        "loadChromePagesState",
        "loadChromePagesStateBrowser",
      ],
    }),
  ],
});

The content script calls store from a vue component:

index.js

import store from "../popup/firstpage/store";

new Vue({
   el: overlayContainer,
   store,
    render: (h) => h(Overlay, { props: { isPopUp: isPopUp } }),
});

Overlay.vue

<script>
import { mapState, mapMutations } from "vuex";

export default {
  props: ["isPopUp"],
  data() {
    return {
    };
  },
  computed: mapState(["chromePagesState"]),
  methods: { 
    ...mapMutations(["addWhiteListedItem"]),
    // this gets called in the template
    addToWhiteList() {
        let newItem = initNewItemWithWebPageData();
        this.addWhiteListedItem(newItem);
    },
    },
}
</script>

The background script receives a message and calls a mutation on the store:

background.js

import store from "../content/popup/firstpage/store";

browser.runtime.onMessage.addListener((message, sender) => {
  if (message.type === "storeinit") {   
    store.dispatch("loadChromePagesState");
    return Promise.resolve(store.state[message.key]);
  }
});

Upon opening popup.js, a store mutation is called that sends a message to background.js that calls another mutation in the store:

popup.js

import store from "./firstpage/store";

export function showPopup() {
  const popupContainer = document.createElement("div");

  new Vue({
    el: popupContainer,
    store,
    render: (h) => h(App),
    created() {
      console.log("Calling store dispatch from popup");
      this.$store.dispatch("loadChromePagesStateBrowser");
    },
  });
}

Where App.vue is

<template>
  <div id="app">
    <OtherComponent />
  </div>
</template>

<script>
import { mapActions } from "vuex";
import OtherComponent from "./components/ChromePage.vue";

export default {
  name: "App",
  OtherComponent: {
    VueTabsChrome,
  },
  methods: {
    ...mapActions(["loadChromePagesState"]),
  },
  mounted() {
    // once fully mounted we load data
    // this is important for a watcher in ChromePage component
    console.log("App.vue mounted");
    // this.loadChromePagesState();
  },
};
</script>

Intuitively export default new creates a new instance on every import hence the not being in sync across scripts (since the stores are different objects).

How can the same store be initialized once and used across multiple entry points?

popup.js is opened when the user clicks the extension icon:

enter image description here

(in this case clicks "new tab").



from Does "export default new" produce a new instance of an object or singleton?

No comments:

Post a Comment