1

I'm getting nuts with this error

[vuex] do not mutate vuex store state outside mutation handlers

I have a asynchronous vue/nuxt method that is triggered when the user wants to edit his/her post. I'm using vuex' store to make the link with my API.
I'm using component data to save content changes while the user is doing actions, a basic configuration.

The error came when I was trying to sort my media.
No matter how hard I'm trying to break references, vue keeps shouting at me with its "do not mutate outside handlers" errors.

Here's the code :

async editSouvenir(id, data) {
  try {
    this.isLoading = true;
    if (this.filesToAdd) {
      for (let file of this.filesToAdd) {
        await this.$store.dispatch('media/CREATE_MEDIA', {
          souvenirId: id,
          file,
        });
        this.fileCtn++;
      }
    }

    if (data.mediaTab.length > 0) {
      let newOrder = 1;
      const newMediaTab = this.newMediaTab;
      // browsing media list
      const newArray = data.mediaTab.slice().sort((a, b) => {
        return newMediaTab.indexOf(a) - newMediaTab.indexOf(b);
      });

      // updating order
      for (let i = 0; i < newArray.length; i++) {
        newArray[i].order = i + 1;
      }

      // saving media data
      data.mediaTab = newArray;
      console.log(data.mediaTab);
    }

    this.isLoading = false;
    if (
      !this.createSouvenirError &&
      !this.createMediaError &&
      !this.postMediaError
    ) {
      await this.$store.dispatch('souvenir/EDIT', { id, data });
      this.$router.push({
        path: '/pns-editor',
        query: { selectedYear: data.date.substr(0, 4), souvenir: id },
      });
    } else {
      // @todo gérer l'erreur
    }
  } catch (err) {
    console.log(err);
    // this.isLoading = false;
  }
},

the vuex mutating error is triggered by this line :

newArray[i].order = i + 1;

The array I'm trying to edit is an array of media objects within the post object.

{ 
    id: 1235354,
    title: 'my title',
    mediaTab: [{
       ** my array of objects **
    }]
}

I don't understand where I mutate store state data. I've read several answers concerning vuex mutating error, but i'm not able to match them with my case.

kissu
  • 40,416
  • 14
  • 65
  • 133
fxguillois
  • 75
  • 6
  • 1
    Does this answer your question? [Vuex - Do not mutate vuex store state outside mutation handlers](https://stackoverflow.com/questions/46044276/vuex-do-not-mutate-vuex-store-state-outside-mutation-handlers) – kissu Nov 03 '21 at 17:53
  • Also, this one may be a good read: https://flaviocopes.com/how-to-clone-javascript-object/ – kissu Nov 03 '21 at 17:58

1 Answers1

0

.slice only makes a shallow copy of your array, hence since you're accessing it one level deeper, it is complaining of a mutation because you're still targeting the reference 1 level below.

One solution would be

<script>
import { cloneDeep } from 'lodash-es'

...
const newArray = cloneDeep(data.mediaTab)

It may not have weird behavior as mentioned here and since it's lodash, you can totally import it and know that it's optimized.
Just don't import the whole library, but only the useful method, hence why destructuring + lodash-es.

PS: be careful because .sort also mutates an array.

kissu
  • 40,416
  • 14
  • 65
  • 133
  • thank you, i'm annoyed that the only solution is to use lodash, but if everyone is using that, i assume it's the right thing to do :) I will be careful. :) – fxguillois Nov 04 '21 at 15:57
  • @fxguillois this is not the only solution, but the 99% bulletproof one and an efficient one. Also, this library is super battle-tested and lightweight if you only import the required methods thanks to tree-shaking (as shown in my code snippet). – kissu Nov 04 '21 at 16:02
  • works like a charm. sorry for the delay, i was struggling with a docker bug after lodash addition. – fxguillois Nov 06 '21 at 12:35
  • i talked too quickly. lodash-es seems not compatible as is with nuxt. i tried the transpile option, but kills quills and other codes in the page. trying https://www.npmjs.com/package/lodash.clonedeep now, but docker makes me misery. i'll tell you when it will be fixed. i used your answer yesterday in another site (without docker) and i had no drawbacks. strange. – fxguillois Nov 06 '21 at 14:22
  • @fxguillois lodash is compatible as is normally. Just install `lodash-es`. – kissu Nov 06 '21 at 15:10
  • https://stackoverflow.com/questions/53081769/syntaxerror-unexpected-token-export-when-using-lodash-with-nuxt i currently have this error : "[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build." – fxguillois Nov 06 '21 at 16:21
  • without transpile option, i have this error : "Must use import to load ES Module: /app/node_modules/lodash-es/lodash.js require() of ES modules is not supported. require() of /app/node_modules/lodash-es/lodash.js from /app/node_modules/vue-server-renderer/build.dev.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules. Instead rename lodash.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /app/node_modules/lodash-es/package.json. " – fxguillois Nov 06 '21 at 16:46
  • Btw, the building takes forever with checking all lodash modules. – fxguillois Nov 06 '21 at 16:46
  • @fxguillois nvm, you indeed need to transpile it in `nuxt.config.js` >> `build: { transpile: ['lodash-es'] }` – kissu Nov 07 '21 at 09:49
  • finally i found an alternative, just-clone, that prevents to install all lodash tools. thanks for your help. – fxguillois Nov 08 '21 at 16:31