can't get data from server to NuxtJS Store - vue.js

this is my code :
export const state = () => ({
products: []
});
export const getters = {
getProducts: state => {
return state.products;
}
};
export const mutations = {
SET_IP: (state, payload) => {
state.products = payload;
}
};
export const actions = () => ({
async getIP({ commit }) {
const ip = await this.$axios.$get("http://localhost:8080/products");
commit("SET_IP", ip);
}
});
the server is working nicely but i just can't get the data into the store

First of all, I highly recommend you rename your action and mutation to something like getProducts and SET_PRODUCTS instead of ip. Also make sure you change the variable name inside the action. While this doesn't change any functionality, it makes your code easier to read.
Second, maybe add a console.log(ip) right after you define the const in the action and see if you're getting the data you want in there. In most cases you're going to want to assign ip.data to your variable.
Lastly, make sure you're calling the action somewhere in the code.
You should do it like this:
this.$store.dispatch('getIP'); // Using your current name
this.$store.dispatch('getProducts'); // Using my recommended name

Related

react query with parameters

I have a react query that wraps on of my API calls. I would like to expose a paramter to the user of my custom hook which lets them set the paramter for this specific API call.
How can I do that idiomatically?
My current custom hook looks like this:
const useGamesApi = () => {
const [games, setGames] = useState<Game[]>([]);
const upcomingGamesQuery = useQuery(
["upcoming", date],
async ({ queryKey }) => {
const [_, date] = queryKey;
const ret = await apiGetUpcomingGames(date);
return ret;
},
{
onSuccess: (data) => {
setGames(data);
},
}
);
return {
games: games,
};
};
export default useGamesApi;
This doesn't expose the date parameter as I would want it, since there is no external way of modifyin that date parameter.
You can just pass the parameter in braces, just like functions. Then user would be able to use it like useGamesApi(key)

Nuxt store getter not working, ID given to payload is not an Integer + Error: [vuex] do not mutate vuex store state outside mutation handlers

I am trying to make a product detail page. The detail page is named _id.
When opened the id is replaced with the product id. On opening the page the state is set with data fetched from an api.
After that i am trying to use a computed property that refers to a getter named getProduct() with an id (this.$route.params.id) in the payload.
This is how my _id.vue looks like:
methods: {
...mapActions("products", ["fetchProducts",]),
...mapGetters("products", ["getProduct",]),
},
async mounted() {
this.fetchProducts()
},
computed: {
product() {
return this.getProduct(this.$route.params.id)
}
}
This is how my store file named products.js looks like:
import axios from "axios"
export const state = () => ({
producten: []
})
export const mutations = {
setProducts(state, data) {
state.producten = data
}
}
export const getters = {
getProduct(state, id) {
console.log(id)
return state.producten.filter(product => product.id = id)
}
}
export const actions = {
async fetchProducts({ commit }) {
await axios.get('/api/products')
.then(res => {
var data = res.data
commit('setProducts', data)
})
.catch(err => console.log(err));
}
}
What works is creating the state, but when i try to use the getter something goes wrong.
As you can see i console.log() the id given to it. Which logs the following:
I also get the error: client.js?06a0:103 Error: [vuex] do not mutate vuex store state outside mutation handlers.
Which I'm not doing as far as I know?
**Note: **these errors get logged as much as the length of my state array is.
From the Vuex documentation:
Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. Like computed properties, a getter's result is cached based on its dependencies, and will only re-evaluate when some of its dependencies have changed.
Like computed, getters does not support having arguments.
But there is a way to have "method-style access" to a getter: https://vuex.vuejs.org/guide/getters.html#property-style-access
You can also pass arguments to getters by returning a function. This is particularly useful when you want to query an array in the store:
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
Note that getters accessed via methods will run each time you call them, and the result is not cached.

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')

Vuex populate data from API call at the start

apologies for the simple question, I'm really new to Vue/Nuxt/Vuex.
I am currently having a vuex store, I wish to be able to populate the list with an API call at the beginning (so that I would be able to access it on all pages of my app directly from the store vs instantiating it within a component).
store.js
export const state = () => ({
list: [],
})
export const mutations = {
set(state, testArray) {
state.list = testArray
}
}
export const getters = {
getArray: state => {
return state.list
},
}
I essentially want to pre-populate state.list so that my components can call the data directly from vuex store. This would look something like that
db.collection("test").doc("test").get().then(doc=> {
let data = doc.data();
let array = data.array; // get array from API call
setListAsArray(); // put the array result into the list
});
I am looking for where to put this code (I assume inside store.js) and how to go about chaining this with the export. Thanks a lot in advance and sorry if it's a simple question.
(Edit) Context:
So why I am looking for this solution was because I used to commit the data (from the API call) to the store inside one of my Vue components - index.vue from my main page. This means that my data was initialized on this component, and if i go straight to another route, my data will not be available there.
This means: http://localhost:3000/ will have the data, if I routed to http://localhost:3000/test it will also have the data, BUT if i directly went straight to http://localhost:3000/test from a new window it will NOT have the data.
EDIT2:
Tried the suggestion with nuxtServerInit
Updated store.js
export const state = () => ({
list: [],
})
export const mutations = {
set(state, dealArray) {
state.list = dealArray
}
}
export const getters = {
allDeals: state => {
return state.list
},
}
export const actions = {
async nuxtServerInit({ commit }, { req }) {
// fetch your backend
const db = require("~/plugins/firebase.js").db;
let doc = await db.collection("test").doc("test").get();
let data = doc.data();
console.log("deals_array: ", data.deals_array); // nothing logged
commit('set', data.deals_array); // doesn't work
commit('deals/set', data.deals_array); // doesn't work
}
}
Tried actions with nuxtServerInit, but when logging store in another component it is an empty array. I tried to log the store in another component (while trying to access it), I got the following:
store.state: {
deals: {
list: []
}
}
I would suggest to either:
calling the fetch method in the default.vue layout or any page
use the nuxtServerInit action inside the store directly
fetch method
You can use the fetch method either in the default.vue layout where it is called every time for each page that is using the layout. Or define the fetch method on separate pages if you want to load specific data for individual pages.
<script>
export default {
data () {
return {}
},
async fetch ({store}) {
// fetch your backend
var list = await $axios.get("http://localhost:8000/list");
store.commit("set", list);
},
}
</script>
You can read more regarding the fetch method in the nuxtjs docs here
use the nuxtServerInit action inside the store directly
In your store.js add a new action:
import axios from 'axios';
actions: {
nuxtServerInit ({ commit }, { req }) {
// fetch your backend
var list = await axios.get("http://localhost:8000/list");
commit('set', list);
}
}
}
You can read more regarding the fetch method in the nuxtjs docs here
Hope this helps :)

Structuring a Vuex module

I am having some trouble in trying to keep my Vuex modules clean and I was hoping to receive some insight on how to improve this. I have already split up some mutations and am using actions to compose multiple mutations so I guess that is a good start.
In most examples I see super clean mutations and I have those as well but a lot I needs checks with if statements or other side effects. To provide examples:
My action:
setFilteredData({ state, commit }, payload) {
commit('setFilteredData', payload);
// Check if we need to split up the data into 'hotels' and 'nearby_hotels'.
if (state.filteredData.find(hotel => hotel.nearby_city)) {
commit('splitHotelsAndNearbyHotels', state.filteredData);
}
}
My mutation:
splitHotelsAndNearbyHotels(state, payload) {
// Chunk it up into hotels and nearby hotels.
const composed = groupBy(payload, 'nearby_city');
if (composed.true) {
composed.true.forEach((hotel) => {
if (hotel.isFirst) hotel.isFirst = false;
});
composed.true[0].isFirst = true;
// Merge them back together in the right order.
state.filteredData = composed.false.concat(composed.true);
}
}
In this example if my array of objects contains a hotel with hotel.nearby_city set to true it will perform the commit of splitHotelsAndNearbyHotels.
The code is not transparent enough. The if statement inside the action does not feel right and I would like my mutation to be cleaner.
I have thought about splitting up my splitHotelsAndNearbyHotels into separate functions but I have no idea where to place those. Simply putting them inside the Vuex file does not feel like a big improvement putting them in a separate file could be an option I guess.
How could I clean up my file to improve the readability? Perhaps someone can show me a Vuex example which does not have an ideal scenario like what I am dealing with.
Actually you can move your actions code into getters, it's more clean to use single source and filter it on getter.
But if you insist using action you can move your mutation code inside on action, and restructure your actions code just like this:
Helper.js
This is for provide data and helper functions:
var _ = require('lodash');
const payloadData = [
{"name":"A", "nearby_city":true, "isFirst":true},
{"name":"B", "nearby_city":false, "isFirst":false},
{"name":"C", "nearby_city":false, "isFirst":false},
{"name":"D", "nearby_city":true, "isFirst":false}
];
// assumed nearby_city is boolean
const isNearby = (hotels) => { return !!hotels.find(hotel => hotel.nearby_city === true) };
const groupBy = (items, key) => { return _.groupBy(items, item => item[key]) };
Mutations.js
This is your mutation looks now:
const mutations = {
setfilteredData : (state, hotels) => {
state.filteredHotels = hotels || [];
},
}
Actions.js
And this is your actions, it's fine without moving your functions into separate files.
// separate filter function
const filterNearby = (payload) => {
if(isNearby(payload) === false){
return payload;
}
const composed = groupBy(payload, 'nearby_city');
composed.true.forEach((hotel) => {
if (hotel.isFirst) hotel.isFirst = false;
});
composed.true[0].isFirst = true;
return composed.false.concat(composed.true);
};
const actions = {
setfilteredData: ({state, commit}, payload) => {
/**
* Using separate filter function
*/
commit('setfilteredData', filterNearby(payload));
return;
/**
* Using restructured code
*/
// Check if we need to split up the data into 'hotels' and 'nearby_hotels'.
if(isNearby(payload) === false){
commit('setfilteredData', payload);
return;
}
// Chunk it up into hotels and nearby hotels.
const composed = groupBy(payload, 'nearby_city');
composed.true.forEach((hotel) => {
if (hotel.isFirst) hotel.isFirst = false;
});
composed.true[0].isFirst = true;
// Merge them back together in the right order.
commit('setfilteredData', composed.false.concat(composed.true));
}
};