Search code examples
javascriptvue.jsstorevuex

State Variable triggers mutation error


I have the following pseudo-code in my store module

const state = {
  users: []
}

const actions = {
 addUsers: async ({commit, state}, payload) => {
   let users = state.users // <-- problem
   // fetching new users
   for(let i of newUsersThatGotFetched) {
     users.push('user1') // <-- really slow
   }
   commit('setUsers',users)
 }
}

const mutations = {
  setUsers: (state, { users }) => {
    Vue.set(state, 'users', users)
   }
  }

Now - when I run this code, I get the following error Error: [vuex] Do not mutate vuex store state outside mutation handlers.

When I put strict mode to false - the error is gone - but the process-time is really, really slow - as if the errors still happen but without getting displayed.

The problem seems to be where I commented // <-- problem, because after I change that line to

 let users = []

everything runs flawlessly, but I can't have that because I need the data of state.users


Solution

  • The problem is: users.push('user1'), this is the line that mutates the state.

    Remove anything that mutates the state (writes or changes it) from actions and move that into a mutation.

     addUsers: async ({ commit }, payload) => {
       // fetching new users
       commit('setUsers', newUsersThatGotFetched)
     }
    

    Then add the new users in the mutation.

    const mutations = {
      setUsers: (state, users) => {
        state.users.concat(users);
        // or if you have custom logic
        users.forEach(user => {
          if (whatever) state.users.push(user)
        });
      }
    }
    

    The reason it is slow is related to Strict mode

    Strict mode runs a synchronous deep watcher on the state tree for detecting inappropriate mutations, and it can be quite expensive when you make large amount of mutations to the state. Make sure to turn it off in production to avoid the performance cost.

    If you want to speed up the mutation, you could do the changes on a new array which would replace the one in the state when ready.

    const mutations = {
      setUsers: (state, newUsers) => {
        state.users = newUsers.reduce((users, user) => {
          if (whatever) users.push(user);
          return users;
        }, state.users.slice()); // here, we start with a copy of the array
      }
    }