3

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").

Sebi
  • 4,262
  • 13
  • 60
  • 116
  • 1
    What is `popup.js`? And how is it "opened"? – MinusFour Aug 25 '21 at 22:21
  • 2
    Your problem is most likely that these scripts are not being run under the same context. Like running two different scripts under two different windows. – MinusFour Aug 25 '21 at 22:25
  • @MinusFour edited question; yes that is what I suspect as well; how can I use the same vuex store across multiple entry points? – Sebi Aug 26 '21 at 19:51
  • @Sebi Maybe you could try [vuex-persistedstate](https://www.npmjs.com/package/vuex-persistedstate). Data get stored in the storage and should populate a new instance of the storage in the same browser. Don´t know, if this is what you are trying to achieve. – StevenSiebert Aug 29 '21 at 15:05
  • Yes, shared mutations should do the same thing but the problem is with the store instance; each import of the store seems to create a new object. Hence, they can't be in sync. – Sebi Aug 31 '21 at 19:52
  • 1
    `export const { $store } = Instance` in your `index.js`, where `Instance` is what gets returned by `new Vue()`. You can then `import { $store } from './path-to-index.js'` anywhere you need to use that store instance. Somewhat unrelated, but using the same principle: https://stackoverflow.com/a/61902381/1891677 – tao Sep 04 '21 at 22:56
  • @tao thanks; will try and get back (why did you delete the answer?) – Sebi Sep 05 '21 at 21:12
  • 1
    Because you didn't react to it. I only posted it for you. Your question is too specific to help anyone else. It's borderline off-topic. – tao Sep 06 '21 at 01:46
  • This question is not too specific. I am having a very similar issue. A detailed answer would help. – Kelcey Wilson Jul 14 '22 at 23:34

0 Answers0