I have a simple lesson creator where I allow teachers to make selections for various categories, those selections ids are collected and grouped together and I want to return them all at the end as a lesson plan.
However, I am having a strange problem that I can't figure out. My Vuex store shows the selections correctly, however my getter duplicates all of my arrays.
After selections are made, my Vuex store shows something like this through the Vue.js dev-tools plugin:
lesson_store:Object
lesson:Object
selected_event:1
selected_exploration:Array[1]
0:1
selected_extensions:Array[1]
0:1
selected_goals:Array[1]
0:54
selected_lexis:Array[1]
0:2
store.js state and getter:
const state = {
lesson: {
selected_event: '',
selected_exploration: [],
selected_extensions: [],
selected_goals: [],
selected_lexis: [],
}
};
getSelections(state) {
console.log('GETTER SELECTIONS', state.lesson);
return state.lesson
}
My call to getSelections from lesson.vue file:
<template><button #click="saveLesson">Save</button></template>
methods: {
saveLesson () {
console.log('GET RETURN OF SELECTIONS',this.$store.getters["getSelections"]);
},
}
Now my console output is:
lesson_store:Object
lesson:Object
selected_event:1
selected_exploration:Array[2]
0:1
0:1
selected_extensions:Array[2]
0:1
0:1
selected_goals:Array[2]
0:54
0:54
selected_lexis:Array[2]
0:2
0:2
The thing is, none of my other getters behave this way. This getter is super basic.
When I check out store and getSelections getter in the Vue.js dev-tools the values are correct and there are no duplicates.
Any advice or direction you can provide would be much appreciated.
UPDATE::::::
Actions and Mutations for Lesson_Store
// create mutations
const mutations = {
setSelectedEvent(state, payload) {
// state.lesson.selected_event = payload
if (state.lesson.selected_event === payload) {
state.lesson.selected_event = '';
} else {
state.lesson.selected_event = payload
}
},
setSelectedReading(state, payload) {
if (state.lesson.selected_reading === payload) {
state.lesson.selected_reading = '';
} else {
state.lesson.selected_reading = payload
}
},
setSelectedLexis(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_lexis.includes(payload)) {
state.lesson.selected_lexis = state.lesson.selected_lexis.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_lexis.push(payload);
}
// state.lesson.selected_lexis = payload
},
setSelectedExplorations(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_exploration.includes(payload)) {
state.lesson.selected_exploration = state.lesson.selected_exploration.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_exploration.push(payload);
}
// state.lesson.selected_lexis = payload
},
setSelectedQuestions(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_questions.includes(payload)) {
state.lesson.selected_questions = state.lesson.selected_questions.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_questions.push(payload);
}
// state.lesson.selected_lexis = payload
},
setSelectedPerformances(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_performances.includes(payload)) {
state.lesson.selected_performances = state.lesson.selected_performances.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_performances.push(payload);
}
},
setSelectedExtensions(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_extensions.includes(payload)) {
state.lesson.selected_extensions = state.lesson.selected_extensions.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_extensions.push(payload);
}
},
setSelectedGoals(state, payload) {
// if event is already in array, then remove it with filter
// otherwise push it to the array
if (state.lesson.selected_goals.includes(payload)) {
state.lesson.selected_goals = state.lesson.selected_goals.filter(function (item) {
return item !== payload;
});
} else {
state.lesson.selected_goals.push(payload);
}
},
};
// create actions
const actions = {
setSelectedEvent({commit}, payload) {
commit('setSelectedEvent', payload);
},
setSelectedReading({commit}, payload) {
commit('setSelectedReading', payload);
},
setSelectedLexis({commit}, payload) {
commit('setSelectedLexis', payload);
},
setSelectedExplorations({commit}, payload) {
commit('setSelectedExplorations', payload);
},
setSelectedQuestions({commit}, payload) {
commit('setSelectedQuestions', payload);
},
setSelectedPerformances({commit}, payload) {
commit('setSelectedPerformances', payload);
},
setSelectedExtensions({commit}, payload) {
commit('setSelectedExtensions', payload);
},
setSelectedGoals({commit}, payload) {
commit('setSelectedGoals', payload);
},
};
All of these appear to be working correctly because my vuejs dev tools display all of the selection id's properly.
To anyone having similar issues where your dev tools store does not match your actual store values output, it is probably due to your code not updating the store values formally through the actions and mutations approach.
if this store value is ever updated directly without actions and mutations the value in the store will change, however, those updated values will not be detected by vuejs dev tools and your actual store data and dev tools data values will not match.
Related
I retrieve data from the backend in a method. How can I auto render that data in a component when the app starts?
I tried with mount and beforemount.
Is there any other way?
state() {
return {
FirebaseList: []
}
},
mutation: {
firebaseList(state,items) {
state.FirebaseList.push(items)
},
},
getters: {
getFirebaseList(state) {
return state.FirebaseList
}
}
beforemount() {
return this.dataitems
},
methods: {
dataitems() {
db.collection("Orders")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc)=> {
// console.log(doc.id, " => ", doc.data());
var list= doc.data()
this.$store.commit('firebaseList',list)
});
});
},
}
This code is working, but, whenever I re-open the page, it mounts the data again and I see duplicate data on the screen.
I solved it by simply checking that if the list has some items, then don't push the items. Which stopped the list from duplicating items.
if (this.$store.state.FirebaseList.length===0){
// run the fetch code
}
else {
return
}
I'm using apollo within my vue.js application, I'm currently trying to remove an object by running a mutation, here is the code :
this.$apollo.mutate({
mutation: require("../graphql/deleteTag.gql"),
variables: {
id: idToDelete,
},
update: (store, { data: { delete_tags } }) => {
if (delete_tags.affected_rows) {
const data = store.readQuery({
query: require("../graphql/fetchDevices.gql"),
});
data.device_id_to_tag_id = data.device_id_to_tag_id.filter((x) => {
return x.id != tag.device_id_to_tag_id.id;
});
store.writeQuery({
query: require("../graphql/fetchDevices.gql"),
data,
});
}
},
});
And my deleteTag.gql file :
mutation delete_tags($id: Int!){
delete_extras_taggeditem(where: { id: { _eq: $id } }) {
affected_rows
}
}
But when I run this the following error appears :
I don't really know what's going on because I followed the Hasura vue.js documentation...
Thanks in advance for your help !
You can specify the name of the returned key in graphql if you want your result data to be called just delete_extras instead of delete_extras_taggeditem:
mutation delete_tags($id: Int!){
delete_extras: delete_extras_taggeditem(where: { id: { _eq: $id } }) {
affected_rows
}
}
but right now, you query do not return you a
I believe you are missing optimisticResponse parameter in mutate. the "update" function takes 2 passes - first with data from optimisticResponse, and then the data from the actual mutation response.
e.g. something like...
this.$apollo.mutate({
mutation: require("../graphql/deleteTag.gql"),
variables: {
id: idToDelete,
},
optimisticResponse: {
delete_extras_taggeditem: {
__typename: 'extras_taggeditem',
id: -1,
affected_rows
}
},
update: (store, { data: { delete_extras_taggeditem } }) => {
if (delete_extras_taggeditem.affected_rows) {
const data = store.readQuery({
query: require("../graphql/fetchDevices.gql"),
});
data.device_id_to_tag_id = data.device_id_to_tag_id.filter((x) => {
return x.id != tag.device_id_to_tag_id.id;
});
store.writeQuery({
query: require("../graphql/fetchDevices.gql"),
data,
});
}
},
});
https://apollo.vuejs.org/guide/apollo/mutations.html#server-side-example
Also, generally speaking I would always return id in your responses back for any level of resource. Apollo relies on __typename + id to maintain and manipulate its cache.
I'm having trouble figuring this out with the vuex store. I have a created a subscription form to be able to enter your email address and I want to make a post request to the backend api but the data is not showing when I console.log it. It only console.logs the item that has been dispatch to the store and when I'm trying to mutate the item to the state.emailAddress and try to get the data that to the POST I get an empty object. enter image description here
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
pizzaItems: [],
shopCart: [],
choosePizzaSize: [],
total: 0,
deliveryCharge: 0,
emailAddress: '',
},
getters: {
deliveryCharge: state => state.deliveryCharge,
pizzaItems: state => state.pizzaItems,
shopCart: state => state.shopCart,
choosePizzaSize: state => state.choosePizzaSize,
checkOut: state => state.total,
},
mutations: {
Add_To_Cart(state, item) {
const shopItem = state.shopCart.find(x => x.id === item.id && x.size === item.size);
if (shopItem) {
shopItem.count += item.count;
} else {
state.shopCart.push(item);
}
},
Price_Total(state, total) {
state.total = total;
},
fetchProducts(state, products) {
state.pizzaItems = products;
},
fetchPizzasize(state, size) {
state.choosePizzaSize = size;
},
free_Shipping(state, freeshipping) {
state.deliveryCharge = freeshipping;
},
subScribeMail(state, item) {
state.emailAddress = item;
},
},
actions: {
fetchData({ commit }) {
axios.get('http://localhost:3000/saltbageproducts').then((response) => {
commit('fetchProducts', response.data.products);
commit('fetchPizzasize', response.data.pizzasize[0].pizzaSize);
})
.catch((err) => {
console.log(err);
});
},
addTocart(context, item) {
context.commit('Add_To_Cart', item);
},
priceCalculation(context, item) {
context.commit('Price_Total', item);
},
changeFreeshippingStatus(context, shipping) {
context.commit('free_Shipping', shipping);
},
emailCheckUp(context, item, state) {
context.commit('subScribeMail', item);
axios.post('http://localhost:3000/saltbagehome', {
email: state.emailAddress,
}).then((reponse) => {
console.log(reponse.data);
}).catch((err) => {
console.log(err);
});
},
},
modules: {
},
});
Acoording Vuex documentation;
Register actions on the store. The handler function receives a context
object that exposes the following properties:
{
state, // same as `store.state`, or local state if in modules
rootState, // same as `store.state`, only in modules
commit, // same as `store.commit`
dispatch, // same as `store.dispatch`
getters, // same as `store.getters`, or local getters if in modules
rootGetters // same as `store.getters`, only in modules
}
And also receives a second payload argument if there is one.
Then in your first parameter of your action u have the vuex context, there should be:
emailCheckUp(context, item) { ... }
instead of
emailCheckUp(context, item, state) { ... }
now to access to vuex context inside of your action, just:
emailCheckUp(context, item) {
context.commit('subScribeMail', item);
...
email: context.state.emailAddress,
...
}
In practice, they often use ES2015 argument destructuring to simplify the code a bit:
emailCheckUp({commit, state}, item) {
commit('subScribeMail', item);
...
email: state.emailAddress,
...
}
I make a component, which show information from database in table. But this information with filters.
Filtering can be by event type and by participant (id: integer type).
When I click the button, I call handleShowClick(). In this function I check: if value of type event isn't null, I get from database events with this type. if value of type event is null, I get all events.
After this I check a participant value. If value isn't null, I call function, which search which events are include this participant. Data from this.state.event show in table in another component.
I haven't problems with event type. But I have problem with participant. When I choose one of participant, table shows correct data for a split second. After this return to prev state (without filter by participants).
How can I fix this issue? I set state to event only in this component
class TestPage extends Component {
constructor(props) {
super(props);
this.state = {
event: [],
searchByType: null,
searchByParticipant: null,
participantToEvent: []
};
this.handleShowClick = this.handleShowClick.bind(this);
this.onHandleEventByTypeFetch = this.onHandleEventByTypeFetch.bind(this);
this.handleParticipantSearch = this.handleParticipantSearch.bind(this);
this.onHandleEventFetch = this.onHandleEventFetch.bind(this);
}
handleShowClick() { // onClick
if (this.state.searchByType !== null) {
this.onHandleEventByTypeFetch(); // select * from ... where type=...
} else {
this.onHandleEventFetch(); // select * from ...
}
if (this.state.searchByParticipant !== null) {
this.handleParticipantSearch();
}
}
handleParticipantSearch() {
const list = [];
this.state.participantToEvent.map(itemP => { // participantToEvent is binding table
if (itemP.parid === this.state.searchByParticipant) {
this.state.event.map(itemEvent => {
if (itemEvent.id === itemP.eventid) {
list.push(itemEvent);
}
});
}
});
console.log(list); // here I see array with correct result
this.setState({ event: list });
}
onHandleEventFetch() {
fetch( ... , {
method: 'GET'
})
.then((response) => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => {
if (data.length === 0) {
alert('nothing');
} else {
this.setState({
event: data
});
}
});
}
onHandleEventByTypeFetch() {
fetch( ... , {
method: 'GET'
})
.then((response) => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => {
if (data.length === 0) {
alert('nothing');
} else {
this.setState({
event: data
});
}
});
...
}
}
Structure of this.state.event:
[{id: 1, name: 'New event', participant: 5, type: 10}, ...]
Structure of this.state.participantToEvent:
[{id: 1, idparticipant: 5, idevent: 1}, ...]
this.setState(...this.state,{ event: list });
I think this would solve your problem. Because you clear every item except for {event:list} by not copying the previous state.
Edit:
You should put
...this.state
to onHandleEventByeTypeFetch and onHandleEventFetch. Without them when you click handleShowClick one of those two functions always work and clears searchByParticipant data from the state by not copying the previous state.
The reason for you see the correct data for a short time is all about async nature of the state.
Let's say I have a POST request that updates the status of a property in an object to true / false.
In my reducer, I have this dispatched as an action after that post request is successful.
case "ADD_CHANGE_TO_TRUE_RESOLVE":
if (action.res.errorCode == 0) {
console.log("NO ERROR!");
// DO SOMETHING
}
Currently, the status can be changed in the backend but unless I refresh the page, then the 'true' status isn't reflected in the DOM (am doing a GET request on componentDidMount() to get the latest data when the page refreshes).
My question is how do I change the state in the store immediately after the post request (POST request is triggered by a click of a button)?
Do I do this?
case "ADD_CHANGE_TO_TRUE_RESOLVE":
if (action.res.errorCode == 0) {
console.log("NO ERROR!");
const objChanged = newState.list.find(function (obj) { return obj.id === action.res.data.id; });
objChanged.is_favorite = true;
Considering that I have this as my state in the store:
const currentState = [
{
list: []
}
];
I'm very new to redux / react, so am not sure if I'm doing it properly.
You shouldn't mutate the state value directly. Also while dispatching the actions after a successful API request, you can just pass the id as the payload for the request
dispatch({
type: 'ADD_CHANGE_TO_TRUE_RESOLVE',
id: res.data.id
})
Now in the store, you can process like
case "ADD_CHANGE_TO_TRUE_RESOLVE": {
const objIndex = state.list.findIndex(function (obj) { return obj.id === action.id; });
if(objIndex > -1) {
// return the updated state if the id was found
return {
...state,
list: [
...state.list.slice(0, index),
{...state.list[objIndex], is_favorite: true},
...state.list.slice(index + 1)
]
}
}
// otherwise return the state as it is
return state
}
you can store state as
this.setState({[stateName]:value});
in case you are using ajax or axios, you have to bind the function first like
this.apicallfunction = this.apicallfunction.bind(this);
By passing the id in action, you can update it using 'react-addons-update':
import update from 'react-addons-update'
case "ADD_CHANGE_TO_TRUE_RESOLVE": {
if(action.id > -1){
return update(state, {
list: {
[action.id]: {
is_favorite: { $set: true }
}
}
})
}
else{
return state
}
}