NuxtJS dispatch is not loading data - vue.js

I've been struggling for 5 hours with the following issue.
I have a service file where I have API calls using Axios. In the store, I have an action that uses the service to pull a list of schools, then I commit the data to the mutations. If I console log the data on the mutation object, it works correctly and shows the data. However, when I call dispatch from the component inside the onMounted hook, I get an empty object. Any help is greatly appreciated. (see the code below)
store/schools.js
export const state = () => ({
mySchools: []
});
export const mutations = {
getSchools(state, data) {
state.schools = data;
console.log(state.schools); // works;
}
};
export const actions = {
async getMySchools({ commit }) {
await this.$getSchools().then(response => {
commit("getSchools", response.data);
});
}
};
portal/dashboard.vue
import {onMounted, ref, useStore} from "#nuxtjs/composition-api";
export default {
layout: 'portal',
setup() {
const store = useStore();
const schools = ref([]);
onMounted(async() => {
await store.dispatch('schools/getMySchools'); // is not pulling data
schools.value = store.state.schools.mySchools;
console.log(schools); // empty
});
return {
schools
}
}
};
Thank you

You shouldn't use await with then
try this
async getMySchools({ commit }) {
const response = await this.$getSchools();
commit("getSchools", response.data);
}
I'm assuming that your this.$getSchools() actually works since I'm not sure what that is and it's not part of the code

Related

How to pass reactive data to Vue components using pinia store elements?

GamePage.vue
Destructured the pinia state elements and action method
const $store = useGameStore();
const {game, teamOne, teamTwo} = storeToRefs($store);
const { getGame } = $store;
Passed the destructed variables to components
<player-stat-table
:title="teamTwo.name"
:players="teamTwo.players"
:teamColor="teamTwo.team_color"
/>
Table Display
store/game_store.js
I am trying to edit data from the above table using updatePlayer action, after successfully completing the action I am updating the entire store data by recalling the get action method. But the data in the table is not updating reactively, it's updating after page reload. How to update it reactively?
import { api } from 'boot/axios'
import { defineStore } from 'pinia'
import { splitPlayers } from 'src/helpers'
export const useGameStore = defineStore('game', {
state: () => ({
game: null,
teamOne: null,
teamTwo: null,
}),
getters: {
getTeamOne: state => state.teamOne,
getTeamTwo: state => state.teamTwo,
getGameData: state => state.game,
},
actions: {
getGame(payload) {
return new Promise((resolve, reject) => {
api.get(`/games/${payload.gameID}/`)
.then(resp => {
const data = resp.data;
const teams = splitPlayers(data)
this.game = data
this.teamOne = teams[0]
this.teamTwo = teams[1]
resolve(data)
})
})
},
updatePlayer(payload) {
return new Promise((resolve, reject) => {
api.put(`/playerstat/${payload.id}/`, data)
.then(resp => {
const data = resp.data;
this.getGame({gameID: data.game})
resolve(data)
})
})
},
}
})
First, you can get rid of you getters, cause due to pinia documentation,
as getters you can think of as the computed properties
and you're not computing anything. So you can simply access the state properties, what you are already doing in your GamePage.vue file.
Secondly, you should also consider async/await pattern instead of Promiste.then(). Like mentioned in the comments, there's a problem with promise constructor antipattern in the OP.
I also prefer writing my pinia stores with the setup() approach, because I think it fits the vue3/composition-api approach a bit better.
import { api } from 'boot/axios'
import { defineStore } from 'pinia'
import { splitPlayers } from 'src/helpers'
export const useGameStore = defineStore('game', () => {
const game = ref(null);
const teamOne = ref(null);
const teamTwo = ref(null);
const getGame = async (gameId) => {
const resp = await api.get(`/games/${gameId}/`);
const teams = splitPlayers(resp.data)
game.value = resp.data
teamOne.value = teams[0]
teamTwo.value = teams[1]
};
const updatePlayer = async (data) => {
const resp = await api.put(`/playerstat/${data.id}/`, data)
const gameId = resp.data.game;
await getGame(gameId)
};
return {
game,
teamOne,
teamTwo,
getGame,
updatePlayer
}
});

Vue3 / Vuex State is empty when dispatching action inside of lifecycle hook inside of test

We're using the composition API with Vue 3.
We have a Vuex store that, amongst other things, stores the currentUser.
The currentUser can be null or an object { id: 'user-uuid' }.
We're using Vue Test Utils, and they've documented how to use the store inside of tests when using the Composition API. We're using the store without an injection key, and so they document to do it like so:
import { createStore } from 'vuex'
const store = createStore({
// ...
})
const wrapper = mount(App, {
global: {
provide: {
store: store
},
},
})
I have a component and before it is mounted I want to check if I have an access token and no user currently in the store.
If this is the case, we want to fetch the current user (which is an action).
This looks like so:
setup() {
const tokenService = new TokenService();
const store = useStore();
onBeforeMount(async () => {
if (tokenService.getAccessToken() && !store.state.currentUser) {
await store.dispatch(FETCH_CURRENT_USER);
console.log('User: ', store.state.currentUser);
}
});
}
I then have a test for this that looks like this:
it('should fetch the current user if there is an access token and user does not exist', async () => {
localStorage.setItem('access_token', 'le-token');
await shallowMount(App, {
global: {
provide: {
store
}
}
});
expect(store.state.currentUser).toStrictEqual({ id: 'user-uuid' });
});
The test fails, but interestingly, the console log of the currentUser in state is not empty:
console.log src/App.vue:27
User: { id: 'user-uuid' }
Error: expect(received).toStrictEqual(expected) // deep equality
Expected: {"id": "user-uuid"} Received: null
Despite the test failure, this works in the browser correctly.
Interestingly, if I extract the logic to a method on the component and then call that from within the onBeforeMount hook and use the method in my test, it passes:
setup() {
const tokenService = new TokenService();
const store = useStore();
const rehydrateUserState = async () => {
if (tokenService.getAccessToken() && !store.state.currentUser) {
await store.dispatch(FETCH_CURRENT_USER);
console.log('User: ', store.state.currentUser);
}
};
onBeforeMount(async () => {
await rehydrateUserState();
});
return {
rehydrateUserState
};
}
it('should fetch the current user if there is an access token and user does not exist', async () => {
localStorage.setItem('access_token', 'le-token');
await cmp.vm.rehydrateUserState();
expect(store.state.currentUser).toStrictEqual({ id: 'user-uuid' });
});
Any ideas on why this works when extracted to a method but not when inlined into the onBeforeMount hook?

How to update API path dynamically in VUEX state

I am trying to dynamically update the API path in my Vuex state. Vuex must have a default path "example.com/api/datasetA.json" set when the page loaded and I want to update the path to "example.com/api/datasetB.json" by the user interaction and fetch the new API data immediately.
The relevant part of my code is as follows (updated code):
VUEX:
export const state = () => ({
apiData: [],
apiId: 'datasetA.json'
});
export const mutations = {
fillApiData: (state, data) => {state.apiData = data},
updateApi: (state, newApiId) => {state.apiId = newApiId;}
};
export const actions = {
async getApiData({commit, state}) {
const response = await this.$axios.$get('https://example/api/'+state.apiId);
commit('fillApiData', response);
then VUE method as follows:
methods: {
updateApi(apiId) {
this.$store.commit('updateApi', apiId)
}
Create a mutation that changes the vuex state. Then run this mutation(commit) in the getApiData function
export const state = () => ({
apiData: [],
apiId: 'datasetA.json'
});
export const mutations = {
updateAPI(state, newApiId ) {
state.apiId = newApiId;
}
};
export const actions = {
async getApiData({commit, state}) {
const response = await this.$axios.$get('https://example/api/'+state.apiId);
commit('updateValue', response);
commit('updateAPI', 'some.new.datasetB.json');
}
}
I can update the state directly by using this.$store.state.apiId = apiId in methods but I know this is bad practice
You are correct. However, if you would like that approach to update the state outside Vuex, you can use mutations to change the Vuex - This is good practice.
Then you can do
this.$store.commit('updateAPI', 'my new value')

NuxtJS - Prevent fetch if data already exists in state?

I have a portfolio site built using NuxtJS and a headless Wordpress CMS. On several pages, I'm importing a mixin that looks like this:
import { mapActions, mapState } from 'vuex';
export default {
computed: {
...mapState({
galleries: state => state.portfolio.galleries[0],
})
},
methods: {
...mapActions('portfolio', ['fetchGalleries']),
},
async fetch() {
await this.fetchGalleries();
}
}
The Vuex module looks like this:
export const state = () => ({
galleries: [],
});
export const actions = {
async fetchGalleries({ commit }) {
let res = await this.$axios.$get(`${process.env.WP_API_URL}/wp/v2/media`);
const data = res.reduce((acc, item) => {
const { slug } = item.acf.category;
(acc[slug] || (acc[slug] = [])).push(item);
return acc;
}, {});
commit('setGalleries', data);
}
};
export const mutations = {
setGalleries(state, data) {
state.galleries.push(data);
}
};
fetch is being used in the mixin to return data from the api before page load. I noticed however that each time I navigate to a new page, it's running that same fetch and continually adding duplicate data to Vuex state.
How do I prevent fetch from running and continually adding duplicate data to my state if it already exists?
I'm not sure why this was tripping me up so much, but I figured out a very simple solution.
async fetch() {
if (this.galleries.length) return;
await this.fetchGalleries();
}
Just added a conditional return statement as the first line within the fetch function.

redux-thunk: actions are not dispatching

I am trying to build an app in react native that is suppose to take take two inputs by a user and then make a query to an api and get information about the two inputs. I have been having trouble with redux and redux-thunk and specifically with async actions.
This is the code in my app that i am specifically having trouble with
export const fetchData = url => {
console.log("start Fetching");
return async dispatch => { // this is where the problem is
dispatch(fetchingRequest());
try {
const response = await fetch("https://randomuser.me/api/?results=10");
const json = await response.text();
if (response.ok) {
dispatch(fetchingSuccess(json));
console.log("JSON", json);
} else {
console.log("fetch did not resolve");
}
} catch (error) {
dispatch(fetchingFailure(error));
}
};
console.log("Fetched data");
};
Upon debugging the function, I have ended with finding that when the fetchData function is called the function will execute but the async dispatch that is being returned has undefined behavior.
The output in the debugger when the function is called should be
start Fetching
JSON file information/Error
but the output in the debugger is actually
start Fetching
This is the function in which fetchData is called in
_onPress = () => {
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
this is the mapDispatchToProps function that I have added. The problem is i do not know what to add inside the function.
const mapStatetoDispatch = (url, dispatch) => {
return {dispatch(fetchData(url))}; // do not know what to place in body of function
};
i have connected it in the component with
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
these are the action creators that I import, if needed
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
Assuming you have added redux-thunk as a middleware, it looks like the errors are here:
_onPress = () => {
const { fetchData } = this.props;
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
and
const mapStatetoDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url)),
}};