title: How to achieve state normalization in Vuex?
body: <p>I am developing an app also using Vuex and just hit a situation where dealing with nested objects will be a pain, so I am trying to normalize(flatten) the state as much as possible like the example below:</p>
  
  <pre><code>users: {
      1234: { ... },
      46473: { name: 'Tom', topics: [345, 3456] }
  },
  userList: [46473, 1234]
  </code></pre>
  
  <p>My question is: <strong>What's the "best" way to achieve the above when your API response looks like this</strong>:</p>
  
  <pre><code>data: [
      {id: 'u_0001', name: 'John', coments: [{id: 'c_001', body: 'Lorem Ipsum'}, {...}],
      {id: 'u_0002', name: 'Jane', coments: [{id: 'c_002', body: 'Lorem Ipsum'}, {...}],
      {...}
  ] 
  </code></pre>
  
  <p>Assuming for the sake of the example that <code>comments</code> is a submodule of <code>users</code>:</p>
  
  <p><strong>Option 1:</strong></p>
  
  <pre><code>// action on the user module
  export const users = ({ commit, state }, users) =&gt; {
      commit(SET_USERS, users)
      commit('comments/SET_COMMENTS', users)
  }
  
  
  // mutation on the user module
  [types.SET_USERS] (state, users) {
       state.users = users.reduce((obj, user) =&gt; {
           obj[user.id] = {
               id: user.id, 
               name: user.name,
               comments: user.comments.map(comment =&gt; comment.id)
           } 
           return obj
       }, {})
      state.userIds = users.map(user =&gt; user.id)
  },
  
  
  // mutation on the comments module
  [types.SET_COMMENTS] (state, users) {
      let allComments = []
      users.forEach(user =&gt;  {
          let comments = user.comments.reduce((obj, comment) =&gt; {
              obj[comment.id] = comment
              return obj
          }, {})
          allComments.push(comments)
      })
  
     state.comments = ...allComments
  },
  </code></pre>
  
  <p>IMO this option is good because you don't have to worry about resetting the state everytime you change pages(SPA/Vue-Router), avoiding the scenario where for some reason <code>id: u_001</code> no longer exists, because the state is overridden every time the mutations are called, but it feels odd to pass the <code>users array</code> to both mutations.</p>
  
  <p><strong>Option 2:</strong></p>
  
  <pre><code>// action on the user module
  export const users = ({ commit, state }, users) =&gt; {
      // Here you would have to reset the state first (I think)
      // commit(RESET)
  
      users.forEach(user =&gt; {
          commit(SET_USER, user)
          commit('comments/SET_COMMENTS', user.comments)
      })
  }
  
  
  // mutation on the user module
  [types.SET_USER] (state, user) {
      state.users[user.id] = {
          id: user.id, 
          name: user.name,
          comments: user.comments.map(comment =&gt; comment.id)
      }  
      state.userIds.push(user.id)
  },
  
  
  // mutation on the comments module
  [types.SET_COMMENTS] (state, comments) {
      comments.forEach(comment =&gt; {
          Vue.set(state.comments, comment.id, comment)
      })
      state.commentsIds.push(...comments.map(comment =&gt; comment.id)
  },
  </code></pre>
  
  <p>In this situation there's the need to reset state or you will have repeated/old values everytime you leave and re-renter the page. Wich is kinda annoying and more propicious to bugs or inconsistent behaviors.</p>
  
  <p><strong>Conclusion</strong>
  How are you guys tackling such scenarios and advices/best practices? Answers are very appreciated since I'm stuck on these things.</p>
  
  <p>Also, I'm trying to avoid 3r party libraries like the Vue ORM, normalizr, etc because the needs are not that complex.</p>
  
  <p>Thank you,</p>
  
  <p>PS: The code might have errors since I just wrote it without testing, please focus on the big picture.</p>