user2354037
user2354037

Reputation: 195

Add in a Vuex modules a complex object which manages its status internally

I'm using Vuex, as state handler AND as tools to share data between my components which are not necessarily "father-son".

Using Vuex, i want to share a complex object get from an external library. This object has methods to change its attributes.

For this object I do not care to centralize its status, but want a way to share it between components.

I thought (and discarded) different solutions: - disable strict mode. But I want to have it for all other cases, is very useful! - Do not use Vuex. But it is very convenient and in addition it is well integrated with debugging tools (chrome plugin for example) - replace Vuex with a more simple store: https://austincooper.dev/2019/08/09/vue-observable-state-store. Same problems as before

So, what is the best way to share complex data from other libraries? I would like to use vuex, but I can't find the cleanest solution!

An example of my problem: (Press get more items and a console error will appear) https://codepen.io/ale-grosselle/pen/dyyxyMr

class Item{
    id;
    constructor(i){
        this.id = Math.round(i * 100);
    }
}
//Share collection between the different components
class Collection {
    items;
    constructor(){
        this.items = [new Item(Math.random())];
    }   
    getMore(){
        const randomVal = (Math.random());
        this.items.push(new Item(randomVal));
    }
}
const store = new Vuex.Store({
    strict: true,
    state: {
        collection: new Collection()
    },
    mutations: {},
    getters: {
        collection(state) {
            return state.collection
        }
    },     
    modules: {}
})

new Vue({
    el: '#app',
    store,
    computed: {
        collection() {
            return this.$store.getters.collection;
        }
    },
    methods: {
        addNew() {
            this.collection.getMore();
        }
    }
})

Upvotes: 2

Views: 537

Answers (1)

UriannRima
UriannRima

Reputation: 26

Even though I understand your reason to use Vuex to try to share a object through your application, I think that it isn't exactly the purpose, since the objective is to share a Single Source of Data through your application, and you're exactly saying that you don't want (exactly) that.

You could, if you want, share a singleton instance of Collection class, exported by a module, and access it through your application using the Provide/Inject API.

Other problem that you'll have (since I've tried doing almost the same in a project) is that the state of the store has to be serializable, and if you don't make any special treatment, the methods of your Collection will be lost when you use the "time travel" from Chrome DevTools (since the state is serialized and deserialized using JSON.stringify/JSON.parse).

I know that isn't exactly what you wanted, but Vuex doesn't seem to be what your looking to use in your situation.

Edit: I've updated your example by using the idea that I gave above, and used Vue.observable to make the object reactive to Vue, you may check it here:

class Item {
    id;
    constructor(i){
        this.id = Math.round(i * 100);
    }
}

//Share collection between the different components
class Collection {
    items;
    constructor(){
        this.items = [new Item(Math.random())];
    }   
    getMore(){
        const randomVal = (Math.random());
        this.items.push(new Item(randomVal));
    }
}

// Make it observable
const collection = Vue.observable(new Collection());

// Export like a singleton from some module
// export default collection

// provide anywhere on top, for example, in the app itself.
new Vue({
    el: '#app',
    provide: {
        'collection': collection
    },
})

// inject anywhere "below" and use it
const ChildComponent = Vue.component('child-component', {
    template: `
        <button @click='addNew'>Get more items</button>
    `,
    inject: ['collection'],
    methods: {
        addNew() {
            this.collection.getMore();
        }
    }
});

// template
<div id='app'>
    <pre>{{ collection }}</pre>
    <div v-for='item in collection.items'>
        {{ item.id }}
    </div>
    <child-component />
</div>

Upvotes: 1

Related Questions