Search code examples
arraysrecursionmultidimensional-arrayvuexmutation

Multidimensional Arrays, Vuex & Mutations


I'm attempting to both add and remove items in a multidimensional array stored in Vuex.

The array is a group of categories, and each category and have a sub-category (infinity, not simply a two dimensional array).

Example data set is something like this:

[
   {
     id: 123,
     name: 'technology',
     parent_id: null,
     children: [
          id: 456,
          name: 'languages',
          parent_id: 123,
          children: [
             {
                id:789,
                name: 'javascript',
                parent_id: 456
             }, {
                id:987,
                name: 'php',
                parent_id: 456
             }
          ]
        }, {
          id: 333,
          name: 'frameworks', 
          parent_id 123,
          children: [
             {
                id:777,
                name: 'quasar',
                parent_id: 333
             }
          ]
        }
     ]
   }
]

....my question is, how do I best add and remove elements to this array, which is inside of a Vuex Store?

I normally manipulate simple arrays inside the Vuex Store using Vue.Set() to get reactivity. However, because I'm not sure how deep the nested array being manipulated is - I simply can't figure it out.

Here's an example of how I thought I could add a sub-category element using recursion:

export const append = (state, item) => {
  if (item.parent_uid !== null) {
    var categories = []
    state.data.filter(function f (o) {
      if (o.uid === item.parent_uid) {
        console.log('found it')
        o.push(item)
        return o
      }
      if (o.children) {
        return (o.children = o.children.filter(f)).length
      }
    })
  } else {
    state.data.push(item)
  }
}

Solution

  • The first thing to understand is that vuex, or any other state management library based on flux architecture, isn't designed to handle nested object graph, let alone arbitrary/infinity nested objects that you mentioned. To make the matter worse, even with shallow state object, vuex works best when you define the shape of the state (all desired fields) upfront.

    IMHO, there are two possible approaches you can take

    1. Normalize your data

    This is an approach recommended by vue.js team member [here][2].

    If you really want to retain information about the hierarchical structure after normalization, you can use flat in conjunction with a transformation function to flatten your nested object by name to something like this:

    const store = new Vuex.Store({
      ...
      state: {
        data: {
          'technology':                      { id: 123, name: 'technology', parent_id: null },
          'technology.languages':            { id: 456, name: 'languages', parent_id: 123 },
          'technology.languages.javascript': { id: 789, name: 'javascript', parent_id: 456 },
          'technology.languages.php':        { id: 987, name: 'php', parent_id: 456 },
          'technology.frameworks':           { id: 333, name: 'frameworks', parent_id: 123 },
          'technology.frameworks.quasar':    { id: 777, name: 'quasar', parent_id: 333 },
        }
      },
    });
    

    Then you can use Vue.set() on each item in state.data as usual.

    2. Make a totally new state object on modification

    This is the second approach mentioned in vuex's documentation:

    When adding new properties to an Object, you should either:

    • Use Vue.set(obj, 'newProp', 123), or

    • Replace that Object with a fresh one

    ...

    You can easily achieve this with another library: object-path-immutable. For example, suppose you want to add new category under languages, you can create a mutation like this:

    const store = new Vuex.Store({
      mutations: {
        addCategory(state, { name, id, parent_id }) {
          state.data = immutable.push(state.data, '0.children.0.children', { id, name, parent_id });
        },
      },
      ...
    });
    

    By reassigning state.data to a new object each time a modification is made, vuex reactivity system will be properly informed of changes you made to state.data. This approach is desirable if you don't want to normalize/denormalize your data.