This is my first post on Dev.to so I would appreciate any feedback which could help me to improve my overall writing and also things that I might h...
For further actions, you may consider blocking this person and/or reporting abuse
Great post, thanks for taking the time to write it.
I'm trying to watch a Vuex user object and then do a Vuefire query when it gets populated.
I tried your suggestion but the watcher never fires. If I add the immediate flag, it will fire, but then the object I get doesn't seem to be the user object that I'm watching. Weird, right? (Not sure how to tell what object I'm getting, but it doesn't have any of the user fields like id or display name...)
gist.github.com/adamb/6b52d7127b39...
I this example the watcher is called but the object doesn't seem to be the user, because there is no user.id.
Also, the Vuex watcher is never called, but when the beforeDestroy() is called I get an error:
[Vue warn]: Property or method "handles" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
I looked at the docs and I am declaring handles[] so I'm not sure why it's complaining.
Any suggestions on what to try here?
Hey Adam, I'm glad that you liked it!
Let me try to help you from your gist.
You've a computed property called
user
which comes from your store, right? You don'twatch
computed properties (vuejs.org/v2/guide/computed.html). The computed property will have a set/get methods that you can customize for our needs. So you know when it changes (set
is called).If you want to watch, you can still do watch the
getters.user
so whenever the user changes (I'm assuming that is what you want here) this watcher will be called. I've tried to copy from your example the code below and adapt it but please double check the calls.What is happening here is:
getters.user
so I assume you've someuser
state that gets committed and a getter that maps to this stategetters.user
changes (new entry/property - watch out forVue.set
in this case) the watcher is called (where you want to call firestore)I hope it is what you want (as I don't know in details it gets a bit tricky to fully understand the example). Let me know if I helps or let me know if anything else is needed.
Yes, you're correct.
user
is in the store and I'm trying to get data from Firestore whenuser
changes. Butthis.$store.watch
never gets invoked. If Iconsole.log
it inmounted
it's null. But when Iconsole.log
it inbeforeUpdate
it's set. Yet,this.$store.watch
is never called.If I use the normal watcher on
user
it gets invoked, but the values don't seem to be there. I can log them and they are undefined. Perhaps this is becauseuser
is a Vuex computed property. I trieddeep: true
but then the (non-store) watcher is invoked twice, neither time with the data I need.Any idea why the watcher isn't being called even through
user
is obviously changing?Thanks for your help!
Hey Adam, to help our discussion I've created this Codesandbox: codesandbox.io/s/agitated-haze-ojdun
Please check
store.js
andApp.vue
. I've tried to create a fetch logic (full user and just username). Please mind the comments and play with comment/uncomment thedeep
andimmediate
options.Also mind the first warning here vuejs.org/v2/api/#vm-watch once you hit the
Fetch new username
button.This saved me a tonne! Using it to fetch a user's id from the db (which may not be present at start). The user id is then used to determine which chats to show.
Before this, I kept getting
null
exceptions. Now I can just check for whether a value exists on change and act accordingly.Even better, 0 errors now.
Thanks a lot Vin 👌
Great! Really happy to "hear" 😅 that!
Amazing!! ty sm 🧡
thank you very much from thailand.
You are welcome, from Brazil 😅
Very nicely done. I used your information to automatically populate a Bank Name input field whenever a user enters a valid transit routing number (TR) in another input field. The TR is sent to a validation service on the server, which returns a JSON of the bank found, or an error. I watch for a "valid" routing number and populate the Bank Name from the "name" found in the JSON. If the routing is "invalid" then I ignore it and do nothing to the Bank Name.
Hey Fidel, thank you very much!
Really nice use case, thanks for sharing!
Was it easy to setup or did you find any problems in the way? Did you also find something else I could add to this post as a resource maybe?
IMHO there is a very important thing missing. If you want to watch for a FIELD change in an object or an item change in an array, you need to add deep:true to the watch method and use Vue.set to modify the watched object/array/field.
Hey Roland, thanks for your feedback.
That is true. I will update the article and add it (with links to the original docs about this corner case).
Nevertheless I strongly suggest to use
Vue.set
whenever possible to avoid pitfalls :)Hello, may I ask you something.
In above example, you were assigning return function from this.$store.subscribe into this.unsubscribe, also return function from this.$store.watch into this.unwatch.
But you never declare unwatch and unsubscribe in the data property.
That would mean unwatch and unsubscribe is added directly into local "this" in this Vue component.
Is it good practice?
Or should I add something like this,
Hey!
The difference is basically that I'm not watching these values so I don't need to declare them in the
data
property. The downside, of my approach, is lack of visibility (you've no clue, without reading the code, that these values exist). It is definitely better now if you use the Composition API.I hope it helps, let me know if you still have questions!
Thank you. I'm monitoring a total count of video cameras in a reactive 'smart' shopping cart. If that number is greater than 4 then vuex queries the db for the model number of our recommended 8-channel NVR. I'm placing that model number in the state.
Now with your solution, I can just watch the state and replace the NVR in the cart with whatever model number is in the state.
Thank you. Thank you. Thank you.
Super cool man! I think it is very unique use case this one!
Easy to understand and cater the solution to match my specific situation. One note though: I'm using Vuex as a substitute for event emitters and since my state values might not always be changing I had to use the vuex subscribe as watchers only respond to changes.
On that, it seems that Vuex can be really useful when trying to extend the functionality of event emitters; is this a recommended practice?
For example, I'm creating a simple showcase of UI elements and I have a form. The elements that make up the form would essentially be firing off events when they are clicked/modified/etc. As far as I can tell, using event emitters, the only way to respond to these events is to use
v-on:<event name>
on all of the parent elements. Given this:That would mean putting
@:btnClick="<fxn>"
on every element. That just seems inefficient. This use-case is not ideal, but given an application that has a lot of large forms responding to events on every parent and then propagating it up seems like it could be a mess.TLDR:
Thanks!
Hey @allen1556 , first of all thanks for the kind feedback!
Well, I will try my best to understand your issue and help you.
I think you're concern about adding some logic to your button, am I right? From what I understood you want to keep it "pure", which means that is just emits an event and the parent handles it the way it wants but as you've just shown, you might have 10x this button and have to add the props (which are different for each button) + the same handler over and over, right?
If I got it right, adding the handler to your button component might not allow you to use in other places. What you can do is to have a wrapper that handles this event (the wrapper forwards everything down to the component). The
@click
would just dispatch your action for example. So, short answer, yes, it is fine to update your state like that. The wrapping component would have this "store" logic and would make sense just on this part of your application and your button would still work anywhere else you need.Did I get your idea right?
If not, please, let me try it again 😅
Do I need to unsubscribe the mutation in the destroyed hook?
Very good question Oscar.
Yes, you do in case your component gets destroyed and the watcher is not needed anymore. You can read about it here: vuex.vuejs.org/api/#subscribe
I will update the examples and the post to add this information. Thanks for asking!
I'm just curious, could you share your use case? I can imagine something like navigating to a new route where the watcher doesn't make sense anymore but can't think about a lot of other use cases =[
Hi Vinicius,
It's just to try to undesrstand the Observable pattern. When you unsubscribe, you cancel Observable executions and release resources. If you don't do it, you'll probably avoid memory leaks. I don't know if Vuex does that for you.
Hey Oscar,
Exactly. Once you subscribe you have to unsubscribe unless your Subject has some kind of check to remove the Observer in case it doesn't exist anymore.
For Vuex you've to do it manually, which means you've to unsubscribe once your component is destroyed.
If you don't do it you might end up in trouble as you mentioned =]
I didn't get if you were asking or not but I'm just confirming hehehe
In case you've any other question, let me know =]
Thanks!!
Thank you a lot for your post. It's very useful !
Nice you liked it!
If you don't mind, could you describe where you've used (or plan to use) this strategy?
Of course. I'm writing a tool for designing formal models, and I needed a way to display warning messages from my components (e.g, after login/registration etc.).
Cool! Got your idea!
Thanks for sharing :D
Hmm, the usage of 'mounted' hook to fetch API seems to be contrast from what I have read recently.
Are you sure that is the correct place to put API call?
Hey!
Sorry for the late reply, I was on vacation for a week and attending a conference this week!
Yeah, I definitely agree that you might use it in the created hook, I use it like that most of the time. Nevertheless regarding the examples shown above it won’t have an impact in the logic itself. The big difference is if you want to access the DOM or not, or if you have SSR in place as well which can cause some inconsistencies as created hook runs in the server and mounted doesn’t.
Do you think it would add any value updating the examples?
Thanks!
From my previous read, I would love to see we can provide consistent tutorial and samples.
I also use
mounted
hook to fetch API due to non-SSR requirements. But then I'm working on some Nuxt projects so I need to change my own practice.If you can update the examples then it'll be perfect. Thank you.
It took a bit longer than what I expected but just updated the repository with the examples. I'm going to update the examples here as well. Thank you again for pointing it out!
It should be all up-to-date now. I've also checked the text itself to update any references to
mounted
.Hello,
I'm really grateful for this article
But using "computed: mapState(['status'])", denied you to put another computed func,
How did you solve this problem ?
Hey Cheddadi,
If I understood you correctly you want to have this
mapState
+ another computed property, right?If so, you can do it like that:
You can read more about it here: vuex.vuejs.org/guide/state.html#ob...
Hope it helps! Have a nice day!
Hey Neves,
Yes, its work fine now.
I'm just wondering about these three dot (...) !! What they do?
Nice to hear that!
It is called
Destructuring assignment
. You can read more about it here: developer.mozilla.org/en-US/docs/W...In short (from MDN)
Thanks for this, learned a lot. I was wondering if there was a way when using vuex watch to reset the status to 'pending' by a user action in the component?
For example if the user submits incorrect details and gets the error, then clicks back into the form that it would then reset the status back to pending?
Thanks
Or you could set getter as a function call, example:
isSearch: (state: CustomsState) => () => {
return state.isSearch
},
Hey @isabolic99 , sorry but I didn't get your point here. Could you add more context?
Thanks!
Many thanks for your article <3 I've refactored my dirty code with using the info here :)
Hey! Glad it helped!
Could you share your use case and why do you consider your code dirty?
Thanks!
Thanks!
+1 for the subscribe method! Thx!
Very helpful, been looking for this for some time now. Thank you very much
Thanks! Could you share your use case?
I have t say, I keep comming back to this article, great job writing this
Thank you very much!
I really appreciate the feedback!
If you have any tips on how to improve it (more examples, use cases, code rewriting...) I would love to hear!
Thanks!
snipped -> snippet (you could fix this typo)
🙈 thanks! I will fix it =]
Why you use Vue.set(state, 'status', status) instead of just state.status = status?
In this case it is not necessary and I could have use
state.status = status
, no worries.But as I'm more concerned with (vuejs.org/v2/guide/reactivity.html) I've got used to always write
Vue.set
to avoid any pitfalls while developing. So it is just a way to keep the codebase concise (in one mutation it isstate.prop = prop
and in the other isVue.set...
).Hey @tazim404 could you elaborate more your question? Which method are you talking about? Could you maybe share some code?
Thanks!
Thanks. Useful
I got vuex store undefined when my application redirected from PayPal sandbox. The store got undefined and the auth middleware doesn't apply as expected.
Don't know how to fix this?
Hey Abu,
Could you give some extra input here? I didn't fully get what you mean. Could you maybe share a gist where I can try to understand your situation a bit better?
Thanks!