DEV Community

Ibrahim Ezzy
Ibrahim Ezzy

Posted on • Edited on

Using Vue Observable as a lightweight state management alternative to Vuex

Vuex is great for the right application but since it imposes a lot of limitations on how you can change data in your store, it may not be ideal for simpler use cases.

Vue Observable introduced in Vue v2.6.0 could be a lightweight alternative to statement management in smaller applications. I recently used it in a way similar to Vuex ie. reference a state property using getters and manipulate the state using mutations without actually accessing the store directly.

So lets create a store with an object users :

(Sorry about the contrived example, I'm writing for the first time so couldn't think of anything better.)

import Vue from "vue";

const state = Vue.observable({
  users: {
    c6676a9aca4c270086ef31a35cc80446: {
      name: "Ibrahim Ezzy",
      twitter: "3zzy",
      bio: "Software Imagineer. Front-end, UI & Design."
    },
    "4d50982553c3286d65182075c178245f": {
      name: "Tim Apple",
      twitter: "tim_cook",
      bio: "Chief Executive Officer of Apple"
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

A generic setState method to update any object within the state:

const mutations = {
    setState({ object, objectPath, value, upsert = false } = {}) {
        console.log("setState args: ", { object, objectPath, value, upsert });
        if (state[object] === undefined || value === undefined)
            console.error("setState: Invalid Object or Value");
        if (objectPath === undefined) state[object] = value;
        if (objectPath && Array.isArray(objectPath) && objectPath.length) {
            let navigate = [object, ...objectPath.slice(0, -1)],
            valueObj = navigate.reduce((obj, prop) => {
                if (typeof obj[prop] !== "object") {
                if (upsert) {
                    obj[prop] = {};
                } else {
                    console.error(`setState: property '${prop}' doesn't exist`);
                }
                }
                return obj[prop];
            }, state);
            Vue.set(valueObj, objectPath[objectPath.length - 1], value);
        }
    }
    // other specific mutations ...
};
Enter fullscreen mode Exit fullscreen mode

And a generic getState method to get any object from the state:

const getters = {
    getState({ object, objectPath } = {}) {
        if (state[object] === undefined) console.error("getState: Invalid Object.");
        if (objectPath === undefined) return state[object];
        if (objectPath && Array.isArray(objectPath) && objectPath.length) {
            let navigate = [object, ...objectPath.slice(0, -1)],
            valueObj = navigate.reduce((obj, prop) => {
                if (obj[prop] === undefined) {
                console.error(`getState: property '${prop}' doesn't exist`);
                }
                return obj[prop];
            }, state),
            value = valueObj[objectPath[objectPath.length - 1]];
            if (value === undefined) console.error(`getState: Invalid object path`);
            return value;
        }
    }
    // other specific getters ...
};

Enter fullscreen mode Exit fullscreen mode

Now you can access the users like so:

data() {
    return {
        users: getters.getState({
            object: "users"
        })
    };
}
Enter fullscreen mode Exit fullscreen mode

and update (or create) using setState:

methods: {
    updateName(e, id) {
        console.log(e.target.innerText, id);
        mutations.setState({
            object: "users",
            objectPath: [id, "name"],
            value: e.target.innerText
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Probably the most interesting part here are the dynamic getState and setState methods that can create, update, or access deeply nested properties within the state. Of course these functions aren't perfect and they just work with Objects for now, but you get the idea.

Here's a demo on CodeSandbox.

Top comments (4)

Collapse
 
ejwaibel profile image
Erik Waibel

With the amount of code you have to write for the getState and setState methods, it seems like a simple Vuex store would work better, no? With Vuex you could just setup your state with whatever data you want to "observe" and then you can just change it directly through a mutation, which also would enforce not touching the state directly from your component.

Based on the small documenation for Vue.observable, it seems like you would use it when you want to modify state directly and make sure your application is reactive to that change, which would make more sense for small projects that wouldn't need to use Vuex.

Collapse
 
anthonygore profile image
Anthony Gore

Super cool! Btw I think it would be easier to read if you used syntax highlighting on your code snippets.

Collapse
 
kenberkeley profile image
Ken Berkeley

Long long ago there is a main stream Vuex alternative: Vue mixin (compatible with Vue 1.x & 2.x).
For more info: github.com/kenberkeley/vue-state-m...

Collapse
 
svtfrancisco profile image
Francisco

You may should turn it on a small lib... and share with us;