Load state with async action Vuex - javascript

I can't realize how to get it works.
I'm trying to load a propertie (productos) on my data() which has to catch the value from a state.
My component:
data () {
return {
productos: this.$store.state.mStock.productos
}
},
created() {
this.$store.dispatch('fetchProductos')
}
At this point i think it's okay, when i load my component i dispatch my action to load the state on the store.
I think the problem is that the way i fill the state is ASYNC
Store:
import StockService from '#/services/StockService'
export const moduleStock = {
strict: false,
state: {
productos: []
},
mutations: {
setProductos (state, payload) {
state.productos = payload.productos
}
},
actions: {
async fetchProductos ({commit}, payload) {
const resp = await (StockService.getProductos())
var productos = resp.data
commit('setProductos', {productos: productos})
}
}
}
When i load my component, the propertie "productos" on the data() is null, however if i see the 'state.productos' from the Vuex devtools, it has the data!
I'm messed up.

The data() method is only run once.
This might seem to work if when the component and the vue store use the same object instance, but doesn't work in this case because a new array instance is assigned in the store while the component stil has the previous instance (the empty array)
Use computed properties. I recommend using the mapState() helper:
computed: mapState({
productos: state => state.mStock.productos
})
without mapState you'd write:
computed: {
productos() {
return this.$store.state.mStock.productos
}
}

Related

How to access localStorage in store/state, namely in state using Nuxt.js?

I am trying to access localStorage in the store, namely in state.
I understand that there is no access to browser methods/objects in SSR.
I'm trying to do this:
export const state = () => {
if (process.client) {
return {
isAuth: localStorage.getItem('isAuth')
}
}
}
The development console (Vuex) is empty, there is nothing.
And if so:
export const state = () => {
if (process.client) {
return {
isAuth: localStorage.getItem('isAuth')
}
} else {
return {
isAuth: 'SomeData'
}
}
}
Then he sees the server work and isAuth = 'SomeData', but i need "client"
How do I access localStorage?
You could try to get this localstorage data in mounted hook and from there commit this data to your vuex state.

Vuex setting state

I have a prop that I'm binding to a child component. I'm trying to get rid of this and set the value to a data in a vuex global state.
<VideoPip :asset="asset" v-show="pipEnabled === true" />
How can I set this.$store.state.assetVideo; which is by default an empty object equal to the asset value? Would I do this in a computed property?
For reading the video state data, you can just use the mapState helper. For example, in your Video component
import { mapState } from 'vuex'
export default {
name: 'Video',
computed: mapState(['assetVideo'])
}
You can then reference this.assetVideo in your component's methods and assetVideo in its template. This will be reactive to changes in the store.
For setting the value, you should (must) use a mutation. For example
const store = new Vuex.Store({
strict: true, // always a good idea
state: {
assetVideo: {} // personally, I'd default to "null" but that's up to you
},
mutations: {
setVideoAsset: (state, assetVideo) => (state.assetVideo = assetVideo)
}
}
and in your components
methods: {
selectVideo (video) {
this.$store.commit('setVideoAsset', video)
}
}
in your parent component:
// if you dont use namespace
import { mapMutations, mapState } from 'vuex'
export default {
computed: {
...mapState(['assetVideo']),
asset: {
get() {
return this.assetVideo
},
set(newValue) {
this.setAssetVideo(newValue)
}
}
},
methods: {
...mapMutations(['setAssetVideo']),
}
}
store
const store = {
state: {
assetVideo: {}
},
mutations: {
setAssetVideo(state, payload) {
state.assetVideo = payload
}
}
}
in your parent component you can change the state using this.asset = 'something' and it will change in store and anywhere else used
and also you can pass it to child components

View doesn't get updated after updates in store Vue js

I have Store, where I have actions and mutations
My action loadProducts calls the mutation setProducts, where I set values in state and it works fine
const actions = {
loadProducts({ commit }, accountId ) {
Products.where( { accountId: accountId } ).all().then((products) => {
commit("setProducts", {accountId: accountId, products: products.data})
});
},
}
const mutations = {
setProducts(state, {accountId, products}) {
Vue.set(state, 'products', products);
}
}
And then in my component I get the data, using mapGetters and computed
computed: {
...mapGetters([
'products'
]),
products2() {
console.log(this.products)
return this.products;
}}
Everything works, but then I try to update the data, it also works, I get updated state - in Vuex console I can see my updates state and new data, but the view itself won't be updated - so i Have to reload the page, to see the data appears.
I also do it via actions, mutations and getters, in mutations it looks like this:
replace() {
Vue.set(state, key, val);
}
Should you be using mapState instead of mapGetters to directly map the state products to your component? Do you have a getter called products that does some extra work?
...mapState([
'products'
]),

What is the best way to save async data in Vue?

I have a following store module:
const state = {
user: {}
}
const mutations = {
saveData(state, payload) {
state.user = payload
}
}
const actions = {
async loadData({ commit }, payload) {
try {
const res = await axios.get('https://api.example.com/user/1')
commit('saveData', res.data.data)
} catch(e) {
console.log(e)
}
}
}
const getters = {
getData(state) {
return state.user
}
}
Now what's the best way to save the data in component? Is it using watch
import { mapGetters } from 'vuex'
export default {
data() {
return {
user: {}
}
},
computed: {
...mapGetters({
getData
})
},
watch: {
'$store.state.users.users'() {
this.user = this.getData
}
}
}
... or store.subscribe?
import { mapGetters } from 'vuex'
export default {
data() {
return {
user: {}
}
},
computed: {
...mapGetters({
getData
})
},
created() {
this.$store.subscribe((mutation, state) => {
if(mutation.type === 'saveData') {
this.user = this.getData
}
})
}
}
Since you already know about store mapping I suppose you try to have some kind of edit form where you need the actual data taken from the database and also the ability to change this data to later send it back to the database.
You don't need a getter to have a simple reference to a store item. You will be very fine with mapState in your component:
{
computed: {
...mapState({
user: state => state.user,
}),
}
}
So as soon as user changed in the store your component will know about it. And here you can update the object you're editing. Let's rename it to edit to avoid collision:
{
data() {
return {
edit: {},
}
},
computed: {
...mapState({
user: state => state.user,
}),
},
watch: {
user: {
immediate: true,
handler(user) {
this.edit = { ...user }
},
},
},
}
Now edit is updated accordingly even if the component was mounted after the store item was updated (thanks to immediate option), and you can safely modify it in your component without any impact on the store reference.
P.S. Have to mention that in this implementation if you want to have reactivity on fields within edit object, then you need to update the whole edit object on each it's field update like this: this.edit = {...this.edit, [prop]: value}. But if you want it to be the natural Vue way, then first you need to initialize edit with actual object structure, and in the watcher for user perform something like Object.assign(this.edit, user).
It's preferable to use computed properties to access store data and keep it reactive.
It's possible to create a computed property using mapGetters as you do in the shared snipped, however, taking into account the getter is simply returning user from state, I don't think you need a getter at all, you can simply map the value from state by using mapState helper. In this way the component would be simplified to something like as follows:
import { mapState } from 'vuex'
export default {
computed: {
...mapState([
'user'
])
}
}
With the above approach you can reference user as this.user in the component's methods or simply as user in template of the component. Also, since the getter is out of use, you can delete the getter definition from the store (unless you are using it anywhere else).

How do I export a string to another Vue file?

I have a masterData.js file that is a store for my master data, in short the file reads my mongo db data & sends it to other project components. I created a function to export the string in the masterData.js file as:
/ ***************************** MUTATIONS
const mutations = {
exportColumns (payload) {
Object.keys(payload[0]).map(x => { return x; });
}
}
Where payload will store all the rows and payload[0] holds the value of column header names. The output of this chunk of code is like this:
["_id","businessAreaName","businessAreaDisplay","councilDisplay","councilID"]
I want to transfer the values to masterData.vue file. My code on masterData.Vue is:
importColumns ()
{
let payload = {
vm: this,
mutation: 'masterData/exportColumns'
};
}
What else should I add to to check whether the column names are received or not?
If you're trying to access the data in your store from within a component, then you'll want to either just map the state to the component or map a getter to the component. Mutations are used by components (or by actions) to modify the state of the store. So instead you would do something like...
//masterData.js
//assuming this gets rolled up as a module called masterdata to the primary store
//store for payload state
const state = {
payload: null,
}
//allows payload to be set -- not sure how you are retrieving the payload but you can use this to store it however you get it
const mutations = {
setPayload (state, payload) {
state.payload = payload
}
}
//get just the columns
const getters = {
getColumns (state) {
Object.keys(state.payload[0]).map(x => { return x; })
}
}
export default {
state,
mutations,
getters,
}
Then
//masterData.vue
<template>
//...
</template>
<script>
import { mapGetters, mapState } from 'vuex'
export default {
computed: {
//I believe getting state from a store module requires a function like this
...mapState({
payload: function(state) {
return state.masterdata.payload
},
}),
//I think for getters you can just reference the method and it will find it
...mapGetters([
'getColumns',
])
},
}
</script>
This is how you import stuff in a single file component.
<template>
<!-- html stuff -->
</template>
<script>
import Mutations from 'yourModule.js'
export default {
name: 'YourComponent',
props: {},
data(){
return {
foo: 'foo'
}
},
methods{
mymethod() {
Mutations.exportColumn(this.foo);
},
}
}
</script>

Categories

Resources