Nuxt 2: in preview mode, vuex getters do not work - javascript

When in preview mode (#see https://nuxtjs.org/docs/features/live-preview) Vuex getters do not appear to work, at least with data that's fetched.
Accessing the data from the store directly works.
Accessing the data via a getter which then calls the same code from the store, returns undefined.
Data is fetched (technically, using axios) via nuxtServerInit, but the issue is the same if I change that to fetch (using axios) via asyncData from within my template.
These are two simple examples (using actual code that exhibits this issue)
This only happens when preview mode is enabled and using target: static
enablePreview()
Why? Why don't getters work? It's like the state that the getters are accessing is different to the state that I access when I access it directly.
Example 1:
/store/index.js
export const getters = {
getURLData(state) {
return state?.urls ?? [];
},
getPageData: (state, getters) => (url) => {
const a = state?.urls; // [array,of,urls]
const b = getters.getURLData; // undefined
...
}
Example 2:
store/index.js
export const getters = {
getSettings(state) {
return state?.settings;
}
}
whatever.vue
export default {
head() {
const a = this.$store.getters.getSettings; // undefined
const b = this.$store.state?.settings; // {data}
}
}

I think that the problem is in your way of calling vuex getters, try to use MapGetters instead of accessing it directly via this.$store.
import { mapGetters } from 'vuex'
export default {
.....
computed: {
...mapGetters({
settings: 'getSettings',
}),
}
....
}

Related

How to access Vuex store in Vue setup() method?

How do I access the Vuex store in Vue when using the setup() method?
I don't have access to the regular this.$store variable anymore.
According to the composition API documentation you have to use the following syntax:
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// access a state in computed function
count: computed(() => store.state.count),
// access a getter in computed function
double: computed(() => store.getters.double)
}
}
}

How to access plugin from nuxt.js store?

I have this gtag (analytics plugin) that I can access on my components but never on my store.
I would appreciate any opinions. Thanks
plugins/vue-gtag.js
import Vue from "vue"
import VueGtag from "vue-gtag"
export default ({ app }, inject) => {
Vue.use(VueGtag, {
config: {
id: process.env.ga_stream_id
}
})
}
store/gaUserProperty.js
import Vue from "vue"
import { User } from "~/models/user/User"
export const states = () => ({})
const getterObjects = {}
const mutationObjects = {}
Object.keys(states).forEach(key => {
getterObjects[key] = state => state[key]
mutationObjects[key] = (state, value) => (state[key] = value)
})
export const state = () => states
export const getters = { ...getterObjects }
export const mutations = { ...mutationObjects }
export const actions = {
async sendUserProperties({ dispatch, commit }) {
let res = await this.$UserApi.getUser()
if (!(res instanceof User)) {
} else {
// I can access this on my components and pages but for some reason not here....
console.log(this.$gtag)
}
}
}
To import this properly, I would export the instance (or any of its internals) from main.(ts|js):
const Instance = new Vue({...whatever});
// only export what you need in other parts of the app
export const { $gtag, $store, $t, $http } = Instance;
// or export the entire Instance
export default Instance;
now you can import it in your store:
import Instance from '#/main';
// or:
import { $gtag } from '#/main';
// use Instance.$gtag or $gtag, depending on what you imported.
As other answers mentioned, in current Vuex version the Vue instance is available under this._vm inside the store. I'd refrain from relying on it, though, as it's not part of the exposed Vuex API and not documented anywhere. In other words, Vuex developers do not guarantee it will still be there in future versions.
To be even more specific, Vue's promise is that all v2 code will work in v3. But only if you use the exposed API's.
And the discussion here is not even on whether it will be removed or not (it most likely won't). To me, it's more a matter of principle: if it starts with a _ and it's not documented, it translates into a message from authors: "We're reserving the right to change this at any time, without warning!"
You can access the Vue instance through this._vm in the Vuex store, so you would just need to do:
console.log(this._vm.$gtag)
That should do the trick.
According to nuxtjs the plugins are available in the store actions.
https://nuxtjs.org/docs/directory-structure/plugins
Sometimes you want to make functions or values available across your
app. You can inject those variables into Vue instances (client side),
the context (server side) and even in the Vuex store. It is a
convention to prefix those functions with a $.

How to access one Vuex state from another in Nuxt?

I have two Vuex stores inside the Nuxt store directory. I'm not able to access one store's states from another, even through routeState.
Store 1: index.js
export const state = () => ({
token: false
})
export const getters = {}
export const mutations = {}
export const actions = {}
Store 2: api.js
export const state = () => ({
apiBase: 'https://myurl.com/'
})
export const getters = {
getAPI: (state, rootState) => {
// Need the state token from index.js here
},
}
export const mutations = {}
export const actions = {}
Here,
state returns the state variables in api.js, that is apiBase
routeState returns the getters in api.js
this is undefined inside Vuex getters, it doesn't work
How do I access the state or getters from index.js?
The rootState is exposed ad the third argument into a getter.
const getters = {
getApi: (state, getters, rootState) => {
// Access the state token from index.js
rootState.token
}}
Check this page for more details Vuex Docs

react-admin: dispatch action to store meta data from dynamic request

Hi this question is a continue to this one!
I'm getting my routes dynamically via an ajax request (following this article in the official docs "Declaring resources at runtime"), I'm using an async function to return a list of resources from an ajax request.
What is the best way to dispatch an action to store meta data, which I got form ajax request in redux, for later access?
Also when user has not yet logged in, this function will not return anything, after logging in, user will have access to a couple of resources. What is the best way to reload resources?
The best option is to use redux-saga. https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html
Then
export function* async() {
yield fetch(); //your Ajax call function
yield put({ type: 'INCREMENT' }) //call your action to update your app
}
Incase you can't use redux-saga, I like your solution with private variable. You should go ahead with that.
To get this to work, I added a private variable, which I store the data mentioned in the question, and I access it via another function, which I exported from that file.
This gives me what I need, but I don't know if it's the best way to go.
https://github.com/redux-utilities/redux-actions
redux-actions is really simple to setup. Configure the store and then you can setup each state value in a single file:
import { createAction, handleActions } from 'redux-actions'
let initialState = { myValue: '' }
export default handleActions({
SET_MY_VALUE: (state, action) => ({...state, myValue: action.payload})
})
export const setMyValue = createAction('SET_MY_VALUE')
export const doSomething = () => {
return dispatch => {
doFetch().then(result => {
if (result.ok) dispatch(setMyValue(result.data))
})
}
}
Then in your component you just connect and you can access the state value
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
class MyComponent extends React.Component {
render = () => (
<span>{this.props.myValue}</span>
)
}
MyComponent.propTypes = {
myValue: PropTypes.string.isRequired
}
const mapStateToProps = (state) => ({
myValue: state.myState.myValue
})
const mapDispatchToProps = () => ({})
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

Using 1 Axios call for multiple components

I am running a simple Axios call like so:
.get('https://myAPI.com/')
.then(response => {
this.info = response.data
})
And then display the data through a v-for array loop on my components. The problem is that I am running this mounted Axios call on each component I use it for. For example, I have a component for desktop screens that uses this axios call to display data in sidebar, while my mobile screen component uses the exact same axios call too display in a header.
The problem is that I am running multiple calls to the same API since each component is using the mounted axiox function.
Is there a way to run this call once and then utilize the v-for loop on each component?
Use Vuex for such task.
I'll make a very simple example.
Install vuex and axios in your project
later create a file in your project call, store.js.
store.js
import Vue from "vue";
import Vuex from "vuex";
import axios from "axios";
const store = new Vuex.Store({
state: {
info : []
},
mutations: {
updateInfo (state, info) {
state.info = info
}
},
actions: {
fetchData({commit}) {
axios.get('https://myAPI.com/')
.then(response => {
commit('updateInfo', response.data )
})
}
}
})
in your main.js import store.js file
import store from "./store";
new Vue({
...
store,
...
});
in your App.vue dispatch 'updateInfo' action.
App.vue
...
created() {
this.$store.dispatch("fetchData");
}
...
And in the component you want to use the info data component, set:
...
computed: {
info() {
return this.$store.state.info
}
},
...
and use info to render the elements with the v-for directive.
This info refers the array of elements you bring
OK, I've found a way to handle this without Vuex. My example: I have two components TrainingCourseComponent and CertificateComponent.
In TrainingCourseComponent:
data() {
return {
trainings : {},
},
methods:{
loadTrainingCenters(){
axios.get("/trainingCourse")
.then(({data}) => {
this.trainings = data;
Event.$emit('globalVariables', data);
});
}
}
created(){
this.loadTrainingCenters();
}
and you can do this in any other component but in this case CertificateComponent(you can define it in mounted() or created() method it doesn't matter:
data() {
return {
training_courses:{}
}
}
mounted(){
Event.$on('globalVariables', (trainings) => {
this.training_courses = trainings;
});
}
p.s. I guess you know but just in case Event is a global Vue instance defined in app.js that I use for different kind of stuff :)
app.js
/**
* some custom event
*/
window.Event = new Vue();

Categories