How to tell vuefire's firestore to execute after "created" hook? - javascript

I am using vuefire for my new Vue.js project. But there is a problem with initialization...
One of my data property need to be intialized via a method. To do this, I use the created hook of Vue.js. Actually I need the value of this property for my firestore request.
Problem is : the firestore request seems to run before the created hook.
How can I run my firestore requests after the created hook as been run ?
Thank !
data: function () {
return {
currentWeek: null
}
},
firestore: function (currentWeek) {
return {
slots: db.collection('slots').where('start', '>=', this.currentWeek)
}
},
created: function () {
this.currentWeek = moment().startOf('week')
},

this is not the exact code but, basically you want to use the $firestoreRefs to make the query yourself inside of the created lifecycle method
data: function () {
return {
currentWeek: null,
slots : []
}
},
created: function () {
this.currentWeek = moment().startOf('week')
this.slots = this.$firestoreRefs['slots'].where('start', '>=', this.currentWeek)
},

I found that I can solve this issue with watch and binding :
data: function () {
return {
currentWeek: null,
slots: []
}
},
created: function () {
this.currentWeek = moment().startOf('week')
},
watch: {
currentWeek (currentWeek) {
this.$bind('slots', db.collection('slots')
.where('start', '>=', moment(currentWeek).toDate())
}
}
I am pretty sure a better solution can be found...

Related

Value retrived by vue-cookie won't trigger lifecycle method "updated"

I'm trying to save and read multiple json objects from my vue-application with the vue-cookie package (Version 1.1.4). The snippet below runs really well and the json object is saved and retrivied as expected.
However, I noticed as soon as the data is retrieved via cookies and i want to change data inside the object on the web page, the "updated" lifecycle method will not trigger. This behaviour is really awkward as I am only adding the cookieToJson() method to the beforMount() method. The Vue debugger also shows that the value is changed. I think the data is not reactive anymore, but how can I fix this.
data () {
return {
json: {
a: 0,
b: 1,
},
}
},
methods: {
jsonToCookie(name) {
const data = "{\"" + name + "\": " + JSON.stringify(this[name])+"}";
this.$cookie.set(name, data, { expires: '1M' }, '/app');
},
cookieToJson(name) {
const data = JSON.parse(this.$cookie.get(name));
if(data==null) return
for(var i = 0; i < Object.keys(data).length; i++) {
var name = Object.keys(data)[i]
this[name] = data[name];
}
},
beforeMount() {
console.log("beforeMount")
this.cookieToJson("json")
},
updated() {
console.log("updated")
this.jsonToCookie("json")
},
}
In case anyone run into a similar problem, I am using the localStorage now.

update dynamic object in redux using spread operator

I am trying to update an object in redux using spread operator but I am not being able to.
Initial state is an empty object because category is received dynamically from api call.
pages and data are both objects which i want to update using spread operator (or whatever works best)
state = {
[category]: {
pages: {
key: value(array)
},
data: {
key: value(array)
}
}
}
At my reducer I try to update it like this
return {
...state,
[category]: {
...state[category],
pages: { ...state[category].pages, pages },
data: { ...state[category].data, doctors },
total: total,
},
};
but i get "error: TypeError: Cannot read property 'pages' of undefined"
What am I doing wrong and how can I update them correctly?
Because state.category is undefined when you fetch this category for the first time. You can fix it like that:
return {
...state,
[category]: state.category
? {
...state[category],
pages: { ...state[category].pages, pages },
data: { ...state[category].data, doctors },
total: total,
}
: {
pages,
data: doctors,
total,
},
};

Vuex Getters doesn't call state

I trying to return back sorted data (which is the already defined state) in a list with the help of a getter, then assign it to the html list in my vue, but it seems it's empty when I check with the vuex tools.
I don't know what am doing wrong.
Below is my store.js file
export default {
namespaced: true,
state:{
displayChatMessages: [],
},
mutations:{
create(state, payload) {
state.displayChatMessages.push(payload)
},
reset(state){
state.displayChatMessages = []
},
},
actions :{
getAllData:({commit}, payload) => {
commit('create',payload)
},
},
getters:{
filteredChatMessages: state => (chatID) => {
return state.displayChatMessages[0]
.filter(el => el.groupid === chatID).sort((l,r)=> l.timestamp - r.timestamp)
},
},
}
Then, after, I call it in the computed area like below :
...mapGetters('chatMessages',['filteredChatMessages']),
Then , I call the Getter inside my function , like below :
getFilteredMessages: function() {
let vm = this
return vm.filteredChatMessages(vm.groupID)
},
Then afterwards, then I set the getFilteredMessages() to the list , getFilteredMessages() , is also defined in the computed section.
But when I look into my vuex tools , I don't see it as an array :
What am I doing wrong ?

How to fetch multiple conditional queries in Apollo Client React?

I'm using Apollo Client, and for fetching queries I'm using useQuery from the package #apollo/react-hooks.
I would like to accomplish the following:
List of Steps:
Step 1: Fetch a query stage
const GetStage = useQuery(confirmStageQuery, {
variables: {
input: {
id: getId.id
}
}
});
Step 2: Based on the response that we get from GetStage, we would like to switch between 2 separate queries
if (!GetStage.loading && GetStage.data.getGame.stage === "Created") {
Details = useQuery(Query1, {
variables: {
input: {
id: getId.id
}
}
});
} else if (!GetStage.loading && GetStage.data.getGame.stage === "Confirmed") {
Details = useQuery(Query2, {
variables: {
input: {
id: getId.id
}
}
});
}
Step 3: Also when the page loads every time, I'm re-fetching the data.
useEffect(() => {
//Fetch for Change in the Stage
GetStage.refetch();
//Fetch for Change in the Object
if (Details) {
Details.refetch();
if (Details.data) {
setDetails(Details.data.getGame);
}
}
});
Problem?
Rendered more hooks than during the previous render.
Details.data is undefined
So how can we call multiple async queries in Apollo Client?
As Philip said, you can't conditionally call hooks. However conditionally calling a query is very common, so Apollo allows you to skip it using the skip option:
const { loading, error, data: { forum } = {}, subscribeToMore } = useQuery(GET_FORUM, {
skip: !forumId,
fetchPolicy: 'cache-and-network',
variables: { id: forumId },
});
The hook is called, but the query isn't. That's a lot simpler and clearer than using a lazy query for your use case in my opinion.
The rules of hooks say you can't conditionally call hooks, whenever you find yourself in a situation where you want to use an if/else around a hook you're probably on the wrong track.
What you want to do here is to use a lazyQuery for everything that's "optional" or will be fetched later - or for queries that depend on the result of another query.
Here's a quick example (probably not complete enough to make your entire code work):
// This query is always called, use useQuery
const GetStage = useQuery(confirmStageQuery, {
variables: {
input: {
id: getId.id
}
}
});
const [fetchQuery1, { loading1, data1 }] = useLazyQuery(Query1);
const [fetchQuery2, { loading2, data2 }] = useLazyQuery(Query2);
// Use an effect to execute the second query when the data of the first one comes in
useEffect(() => {
if (!GetStage.loading && GetStage.data.getGame.stage === "Created") {
fetchQuery1({variables: {
input: {
id: getId.id
}
}})
} else if (!GetStage.loading && GetStage.data.getGame.stage === "Confirmed") {
fetchQuery2({variables: {
input: {
id: getId.id
}
}})
}
}, [GetState.data, GetStage.loading])

VueJS return value from promise in method section of component

The function below set the this.personStatus on all components v-bind. How can I make it update a single comm-person component or multiple comm-person components depending on the result of the axios.post?
The template extract for a person is this:
<comm-person
v-for="person in people"
v-show="(!pollActive) || isParticipant(person)"
:participant="pollActive && isParticipant(person)"
:status="personStatus"
:key="person.id"
>
<div class="checkbox-wrap"><input :disabled="pollActive" :value="person" v-model="selectedPeople" type="checkbox"></div>
<div #click="togglePerson(person)" class="name">{{person.given_name}} {{person.family_name}}</div>
<div class="phone-number">{{person.phone}}</div>
</comm-person>
E.G: :status="personStatus"
data() {
return {
pollText: '',
primaryButton: {
border: `2px solid ${iiColor('ii-green')}`,
},
secondaryButton: {
border: `2px solid ${iiColor('ii-grey')}`,
},
people: [],
safeUsers: [],
unsafeUsers: [],
selectedPeople: [],
polledPeople: [],
pollActive: false,
messages: [],
msgType: 'SMS',
personStatus: '?'
};
},
..
..
methods: {
getStatus(person) {
axios.post(`${config.iiApiUrl[process.env.NODE_ENV]}/person/find`, {
id: person.id,
}).then((resp) => {
console.log('handle response...');
console.log(resp.data.ref);
if(resp.data.ref != null) {
if (this.safeUsers.includes(resp.data.ref)) {
console.log('person is safe');
this.personStatus = 'safe';
}
if (this.unsafeUsers.includes(resp.data.ref)) {
console.log('problem with person');
this.personStatus = 'safe';
}
}
else {
return '?';
}
});
},
}
But the this.personStatus from the axios.post call updates all the components. How can I make it update one or two depending on the result?
Please help!!
First of all post returns a promise and it is performed asynchronously. getStatus will return before the anonymous function you pass in to post will be called. It cannot return the string value. For the caller of getStatus() to get the value of the returned string, you must use then(). I suggest you research asynchronous programming and promises in javascript a bit further to understand callbacks and when they are run.
Secondly, you typically should not bind a prop to the result of a method/function. Props almost always should be bound to a data field or computed.
I suggest you change the post handler to set a data field and bind to that instead.
:status="personsStatus"
data: {
return {
person: ??, //i dont know where this comes from
personStatus: 'unknown', //some default value. maybe '?'
};
},
mounted() {
this.loadStatus(this.somePerson);
},
methods: {
loadStatus(person) {
axios.post(...)
.then((resp) => {
//your more complex logic here,
//but instead of returning a string,
//set the personStatus field like the following:
this.personStatus = 'safe';
});
}
}
This may require you to create a child component to do this work if you have multiple person instances.

Categories

Resources