davestewart / vuex-pathify

Vue / Vuex plugin providing a unified path syntax to Vuex stores
https://davestewart.github.io/vuex-pathify
MIT License
1.37k stars 57 forks source link

[Question] sync() usage #31

Closed robsontenorio closed 6 years ago

robsontenorio commented 6 years ago

Question

How is the proper way to use sync() with Nuxt vuex store?

<template>
  <section>
    <div><h1 class="title">NUXT</h1></div>
    <input v-model="user.age" />
  </section>
</template>

<script>
import { sync } from "vuex-pathify";

export default {
  computed: {
    user: sync("user")
  }
};
</script>

Error

[vuex] Do not mutate vuex store state outside mutation handlers.

How to reproduce

https://codesandbox.io/s/84yrlmoo7l

davestewart commented 6 years ago

Hi Robson,

I'm just looking into this.

It looks like you're trying to sync user not user@* but it's been a while since I looked at the project, so let me do some investigation and get back to you.

There's a Nuxt demo here if you want to compare code in the meantime:

Cheers, Dave

davestewart commented 6 years ago

So I don't have a huge amount of experience with Nuxt, so perhaps you know things I don't, but I think I got to the bottom of this one. It appears to be a combination of Nuxt, and the way you're using Vuex - which is incorrect (at least in strict mode).

So, firstly, the difference between your repo and mine was I appear to be using "classic mode", which is when you export a function which returns a store:

Here's your repo but using classic mode:

It appears if you don't do this, the store is created by Vuex based on your modules. I'm guessing that the reason you were getting the error, is because the plugin is not explicitly included in the store:

plugins: [pathify.plugin],

It appears from a quick google that you can't use plugins unless you use classic mode (feel free to enlighten me!):

So, with that working, we can move on to your usage of both Pathify and Vuex.

Firstly, you're using sync in an attempt to sync the entire user model. Unfortunately, sync doesn't work quite like this! Sync is essentially syntactic sugar to create a compound getter/setter:

sync('user/age')
age: {
  get () { return this.$store.state.user.age },
  set (value) { this.$store.commit('user/SET_AGE', value) }
}

So you can see that syncing the entire User model makes no sense, as you're operating on a class not a property. Instead, you need to use the wildcard syntax to sync all sub-properties:

sync('user/*');

The next thing to look at, is your access of the age property itself.

In your code, you have:

<input v-model="user.age" />

Whilst this does indeed update the model, in strict Vuex terms, it's wrong, as it's NOT using a mutation, rather it's just changing a single property of the User model by reference.

Vuex's strict mode is what picks this up, and what throws the error.

In order to update the store correctly, you need to use the wildcard sync as above, and reference the required property directly...

<input v-model="age" />

...which will fire the compound setter and call the associated mutation (this is what the sync and make.mutations helpers do for you!)

Sorry if this all feels a little complex when you just want this to work, it's just the case of a few wrong assumptions building up to create the problem. Hopefully this points you in the right direction and you can take it from here :)

Updated repo with everything working here:

EmmyMay commented 2 years ago

It pains me to see that Robson did not even reply with a thank you on this issue.

davestewart commented 2 years ago

LOL! It was a pretty thorough answer wasn't it.

Often questions like this are useful to question and review your own pre/as/sumptions, so still valuable without an answer!

Thanks though :D