Vuex: Are multiple store instances supported?

Created on 21 Oct 2016  路  17Comments  路  Source: vuejs/vuex

So I managed to get multiple store instances working, but it feels kind of hacky.

Use case: I'm creating a component that will be distributed in a package, and I want to use Vuex for it's state management. Multiple component instances can exist on a a page, and I want each instance to have it's own Vuex store instance, private from the outside world.

By default, Vuex makes the assumption that you only have one store, which makes sense for apps, but I think it would be very interesting to allow multiple store instances for cases like mine.

I have createStore method which returns a new store instance. Since the modules also need to be unique per instance, I'm cloning them before handing them over to the store.

export default function createStore() {
    return new Vuex.Store({
        modules: {
            media: cloneDeep(media),
            uploads: cloneDeep(uploads),
        },
    });
}

Since every component instance needs it's own store, the store needs to be set in beforeCreate instead of through the component's options.

export default {
    // ...
    beforeCreate() {
        this.$store = createStore();
    },
};

Is what I'm doing safe, or is this something that could break in future updates? Is there any possibility for something like this to be supported?

enhancement

Most helpful comment

I do this:

function createStore () {
  return new Vuex.Store(cloneDeep(options));
}

outside of anything vuejs... then do this inside your root vuejs instance

beforeCreate: function () {
    this.$store = createStore();
  },

And this gets me seperate vuex instances.

All 17 comments

Sounds good to me.
But we should not make multiple store instances in current implementation because of dev tools integration.

Maybe we can introduce devtools: false option for this use case.

Rethinking about it, I think it makes more sense to provide a library module with some namespace like what vuex-router-sync does.
Because, for example, if we want to add some public api later, we can just add an item into the provided module.

Also I think a library should not depends on Vuex if it doesn't have public api related to Vuex.
Because library users may not use Vuex when they use the library. It would be better to create a library specific state manager.

I'm using vue-custom-elements along with vuex to create a redistributable component for work. It would be awesome if we were able to create a separate store instance easily through vuex to work with each separate custom component. I used @sebastiandedeyne's nice little hack and it worked quite nice. Thanks for posting that, and I agree that creating new instances would be awesome!

@ktsn but this solves not the problem, the vuex handling is only transfered from the dumb component the smart parent (which is a good idea), but the main problem still exists: how can multiple stores be created and attached to each component?

I do this:

function createStore () {
  return new Vuex.Store(cloneDeep(options));
}

outside of anything vuejs... then do this inside your root vuejs instance

beforeCreate: function () {
    this.$store = createStore();
  },

And this gets me seperate vuex instances.

This should be part of the Vuex documentation.

@comfroels What is your implementation of the cloneDeep function?

@MrCoder Probably _.cloneDeep(obj).

Using @sebastiandedeyne aproach didn't work with mapState and mapGetters vuex functions, do i need to re wright all the calls which i had used it ?

The community should drive some general solution for case like navigating from list view to a detail view and go back to list view for another detail view. One gotcha I got is

The store should be recreated instead of reinitialized. Reinitialized initial states via mutation will be very costly if you have a large list.
and
You don't want to put you thousands rows of list data in store if you don't need reactivity for them(very likely)

If allows, refreshing the page is a hassle free way. if not, we might need to create a store instance for each detail view component and we might fall into @hoomersinpsom 's question here.

If anyone have best practice about this, please share. Thanks in advance.

@hoomersinpsom @comfroels @sebastiandedeyne Sorry for the necro, but do you have a solution for the problem @hoomersinpsom stated? I have the same problem... As soon as I deepClone the modules, reactiveness (using mapState and mapGetters) stops working...

To improve on @comfroels suggestion this is how you can create standalone components that will have one store for each instance of the component without deep cloning or instancing on beforeCreate

store.js

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex);

export default () => {
    return new Vuex.Store({
        state: {
            //...
        },
        mutations: {
            //...
        },
        actions: {
            //...
        },
    };
}

A .vue component that needs a store instance

import store from "./store"

export default {
    name: "form",
    data() {
        return {
            values: []
        }
    },
    beforeCreate() {
        this.$store = store(); // No way around this
    },
    methods: {
        submit: function () {
           this.$store.commit("submit", this.values);
        },
    }
}

Create your component and store with a factory function like that:

import store from '@/store';
import Component from '@/Component.vue';

export function getComponent() {
    return {
        extends: Component,
        store: store(),
    };
}

Define state in store modules as a function:

export default {
    state() {
        return {
            foo: 'bar',
        };
    },
};

No cloneDeep or this.$store = store stuff!

The "Modules" feature available in Vuex 2.3.0+ supports some of this functionality:
https://vuex.vuejs.org/guide/modules.html

image

I use the store with multiple modules, and I see that is becoming difficult to manage the ALL the modules of vuex altogether. It would be nice to have a system to scope vuex stores (scoped by a main component for example) and create multiple stores that communicates one with each other.

I think you can create modules in store and use them instead
https://vuex.vuejs.org/guide/modules.html

To register a new dynamic module, you can do this:

import store from './store';

const localModule = {
  namespaced: true,
  state() {
    qqq: 222,
  },
  actions: {},
  mutations: {},
  getters: {}
};

const path = 'nameMyModule'; // or ['mainModule, 'subNameModule']

// We initialize the new dynamic module in the global store:
store.registerModule(path, localModule);

// If you want to remove the dynamic module
store.unregisterModule(path);

// Get access to the module context
const { mapState, mapActions, mapMutations, mapGetters } = createNamespacedHelpers(path);

Vue JS DOC: Link

You can use all this inside the vue component. But this does not create a separate store, but a separate module in the global application store.

If you want to make a separate side, then I experimented and got this solution:

import Vuex from 'vuex';
...
created() {
  this.loacalStore = new Vuex.Store({
    state: {
      aaa: 222
    },
    actions: {},
    mutations: {},
    getters: {}
  });
}

But I can鈥檛 access the context (mapState, mapActions, mapMutations, mapGetters), this is a closed area that exists only in this component.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Nickvda picture Nickvda  路  3Comments

niallobrien picture niallobrien  路  3Comments

jbruni picture jbruni  路  3Comments

james-wasson picture james-wasson  路  3Comments

ijse picture ijse  路  3Comments