33

I'm trying to create an object in one part of vuex store, and then pass id to it to another object, and i'm not sure how to properly do that since mutations can't return returning anything (in this case, id).

Two store objects look like this:

// store/report.js
const state = {
    name: 'Untitled Report',
    subReportIds: []
};

// store/subReport.js
const state = { ... }

And i'd like this action to create blank report, then blank subreport, and then assign subreport id to newly created report. (subreports are independent entities, and can be used by multiple reports, hence different area in store)

const actions = {
    createNewReport({ state, commit }) {
        commit(mutationTypes.CREATE_NEW_REPORT)
        // below doesn't work - i can't get return from mutation
        let newSubreportId = commit(mutationTypes.ADD_NEW_SUBREPORT)
        // if this worked, i'd then do something like
        commit(mutationTypes.ADD_SUBREPORT_TO_REPORT, newSubreportId)
    }
};

How can i achieve the above?

Bert
  • 80,741
  • 17
  • 199
  • 164
Rudi
  • 2,450
  • 5
  • 26
  • 37

2 Answers2

26

So best way to accomplish to me would be to dispatch actions instead of committing the mutations. If you look at the methods in Vuex source, commit only executes with no return (so is a void) and dispatch returns the value you return from the action (which is a function)

For my actions, i always return a promise so that i can compose them like you mention above. Here is an example.

fetchSomething ({ commit }) {
  return mockApiGetIds()
    .then(response => {
      commit({
        type: SOME_MUTATION,
        ids: response
      });
    
      return response;
    });
  },
That1Guy
  • 7,075
  • 4
  • 47
  • 59
Austio
  • 5,939
  • 20
  • 34
11

Disclaimer : I don't know if it is truely a good idea, but at least, it seems to work, and to me, it feels prettier than having to use actions and promises, or to generate the id in the action...

With your mutation, you can pass an argument. To return a value from a mutation (like a newly created id), I write it to a placeholder in that argument :

someMutation(state, arg){
   //...
   arg.out = {
      status : "succeed"
   }
}

//...

this.$store.commit('someMutation', arg);
if(arg.out !== "succeed") console.log("ERROR");
hl037_
  • 3,520
  • 1
  • 27
  • 58
  • 3
    Although this does work, relying on mutating an argument to signal the result can increase complexity and is not something that I think would be surprising to other users. – Austio Nov 07 '18 at 13:45
  • ...And this is precisely the reason of the disclaimer. But to a C programmer used to pass a pointer to the object to be mutated, it feels natural. My main concern though is about the possible copy of the object in future versions (even if it's not likely to occur, relying on an undocumented behaviors is always at your own risks). About the complexity, I think writing an action and handling asynchronously the mutation result feels much more complex for me. And it needs 2× more code lines... – hl037_ Nov 08 '18 at 14:16
  • Maybe passing a callback that sets the value instead of passing a plain object would make this solution more readable. But I personally wouldn't use this pattern because after it appears in the code, more and more people realizes that it can be a solution to some of their problems. So they start using it to solve problems that could be solved with more straightforward tools (even if they required more lines/characters) and by the time they realize that it is overused, there's a nice ball of spaghetti in their store. – Szellem Dec 09 '18 at 22:15
  • Don't get me wrong, this is a valid answer for the question, just added my opinion to it because I think it might be useful. – Szellem Dec 09 '18 at 22:21
  • I like the callback idea :) however, I would warn "beginners" it could have unexpected side effects : suppose the callback passed set a reactive property : then you could get a nested mutation and some mess in the logic. the placeholder solution makes it less probable. It's a balance between readability and simplicity. But I totally understand your point of view, and I do think the placeholder solution should be used **only** in such a case of a simple return value (like an error check or returning a generated id like in the previous example) – hl037_ Dec 10 '18 at 02:49
  • The docs advise against callbacks inside mutations: [Mutations Must Be Synchronous](https://vuex.vuejs.org/guide/mutations.html#mutations-must-be-synchronous) – charles-allen May 21 '20 at 00:47
  • 1
    @AjahnCharles you completely misread the documentation. The doc states it should be synchronous, and advises against calling an **ASYNC** function inside the mutation, not against calling a synchronous argument callback. As long as there is no pending **UNRESOLVED PROMISES** that could change the state **AFTER** the mutation **RETURNED**, you're 100% safe. – hl037_ Jun 06 '20 at 14:03
  • @hl037_ My bad; I misunderstood the purpose & usage of the callback. – charles-allen Jun 07 '20 at 05:08