Vue.js - control the router from inside a mutation in Vuex Store - vue.js

I have the following problem:
This is one of my mutations:
tryAutoLogin(state) {
console.log("Trying auto login...");
const token = window.localStorage.getItem("token");
if (!token) {
return;
}
const expirationDate = window.localStorage.getItem("token_exp");
const now = new Date();
if (now >= expirationDate) {
return;
}
state.userData.loggedIn = true;
state.userData.username = token.identity;
// desired: this.$router.push("/dashboard") => results in undefined
}
Currently I commit this mutation inside my component in the created phase of the component:
created() {
this.$store.commit("tryAutoLogin");
this.$router.push("/dashboard");
}
This is not a great way to do it, since I would have to output a value, store it in a variable and use if/else to this this.$router.push("/dashboard").
How can I solve this in an elegant way? Favorable inside the mutation like in the // desired comment. Is there a way to access the router inside the Vuex store?

Pass the vue component instance to the mutation like:
this.$store.commit("tryAutoLogin",this);
in mutation add it as parameter vm then use it as vm.$router.push("/dashboard") :
tryAutoLogin(state,vm) {
console.log("Trying auto login...");
const token = window.localStorage.getItem("token");
if (!token) {
return;
}
const expirationDate = window.localStorage.getItem("token_exp");
const now = new Date();
if (now >= expirationDate) {
return;
}
state.userData.loggedIn = true;
state.userData.username = token.identity;
vm.$router.push("/dashboard")
}

Related

React native changing instance prop not rerender component

I have an entity class called order
const order = new Order({status: 'available'});
export default class Order {
constructor({status}) {
this.status = status;
}
isCanceled() {
return this.status === CANCELED;
}
}
when passing order to a component throw mapStateToProps
when the status changes. mapStateToProps will be called again with the new status but the component will not be rendered with the new data
but if I passed the order as a standard object it will re-render with the new data
This code is not working
const mapStateToProps = (state, props) => {
const order = new Order({status: 'available'});
return {
order,
};
};
This code works
const mapStateToProps = (state, props) => {
const order = new Order({status: 'available'});
return {
order: {...order},
};
};
I need the first code to work as I use some functions from the object inside the component like isCanceled()
Hello all I knew what was the issue
the problem was in my reducer as I was changing in the state directly so the method wasn't pure function. So, react didn't recognize the change in props
this link has an example
React Native components not re-render if props changed
can you try this, while keeping the {...order} (2nd method you're using)
export default class Order {
constructor({status}) {
this.status = status;
this.isCanceled = this.isCanceled; //add this line
}
isCanceled() {
return this.status === CANCELED;
}
}

How to make pagination work? Async await function in vue.js 3 setup

I was trying to make an app which lists a user's repositories from github using github API, however I'm having a big problem with fetching data from all pages (so far I can only get repos from one page). I tried to fix it by using an async/await function (instead of Promise), but it's also my first time using vue3 and I have no idea how to have a function inside of the setup() method.
The current code is here:
https://github.com/agzpie/user_repos
My try at using async/await, which didn't work:
import ListElement from "./components/ListElement";
import { ref, reactive, toRefs, watchEffect, computed } from "vue";
export default {
name: "App",
components: {
ListElement,
},
setup() {
const name = ref(null);
const userName = ref(null);
const state = reactive({ data: [] });
let success = ref(null);
const userNameValidator = /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$/i;
const split1 = reactive({ spl1: [] });
const split2 = reactive({ spl2: [] });
async function myFetch() {};
/*
* Check for input in the form and then fetch data
*/
watchEffect(() => {
if (!userName.value) return;
if (!userNameValidator.test(userName.value)) {
console.log("Username has invalid characters");
return;
}
let hasNext = false;
state.data = [];
do {
async function myFetch() {
let url = `https://api.github.com/users/${userName.value}/repos?per_page=5`;
let response = await fetch(url);
if (!response.ok) {
success.value = false;
throw new Error(`HTTP error! status: ${response.status}`);
}
success.value = true;
// check response.headers for Link to get next page url
split1.spl1 = response.headers.get("Link").split(",");
let j = 0;
while (j < split1.spl1.length) {
split2.spl2[j] = split1.spl1[j].split(";");
console.log(split2.spl2[j][0]);
console.log(split2.spl2[j][1]);
if (split2.spl2[j][1].includes("next")) {
let urlNext = split2.spl2[j][0].replace(/[<>(\s)*]/g, "");
console.log(urlNext);
url = urlNext;
hasNext = true;
break;
} else {
hasNext = false;
}
j++;
}
// second .then
let myData = await response.json();
state.data.push(...myData);
console.log("data", myData);
name.value = "";
}
myFetch().catch((err) => {
if (err.status == 404) {
console.log("User not found");
} else {
console.log(err.message);
console.log("oh no (internet probably)!");
}
});
} while (hasNext);
});
// Sort list by star count
const orderedList = computed(() => {
if (state.data == 0) {
return [];
}
return [...state.data].sort((a, b) => {
return a.stargazers_count < b.stargazers_count ? 1 : -1;
});
});
return {
myFetch,
success,
isActive: true,
name,
userName,
ListElement,
...toRefs(state),
orderedList,
};
},
};
Any help would be highly appreciated
The call to myFetch() near the end is a call to an async function without an await, so it is effectively going to loop (if hasNext was initialized to true, but it isn't) without waiting for it to complete.
You should probably change that line to await myFetch() and wrap it all with a try/catch block.
I also don't really care for the way you're directly updating state inside the async myFetch call (it could also be doing several of those if it looped) and perhaps it should be returning the data from myFetch instead, and then you can use let result = await myFetch() and then make use of that when it returns.
Also, instead of awaiting myFetch() result, you could not await it but push it onto a requests array and then use await Promise.all(requests) outside the loop and it is one operation to await, all requests running in parallel. In fact, it should probably be await Promise.allSettled(requests) in case one of them fails. See allSettled for more.
But also I wonder why you're reading it paged if the goal is to fetch them all anyway? To reduce load on the server? If that is true, issuing them paged but in parallel would probably increase the load since it will still read and return all the data but require multiple calls.

strange console.log output with vuex

i have some simple vuex store with
const state = {
todos : []
}
const getters = {
allTodos: (state) => state.todos
}
const actions = {
async fetchTodos({ commit }) {
console.log(this.state.todos)
if(state.todos.length == 0) {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos?_limit=5')
commit('setTodos', response.data)
}
}
}
const mutations = {
setTodos(state, todos) {
state.todos = todos
}
}
why does console.log in fetchTodos action output populated todos before it was populated with axios.get and setTodos mutation?
when i write
const actions = {
fetchTodos({ commit }) {
console.log(this.state.todos)
setTimeout(async () => {
if(state.todos.length == 0) {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos?_limit=5')
commit('setTodos', response.data)
}
}, 10000)
}
}
output is normal with empty todos in state
That's because you will see a little blue triangle right next to the console log. I don't know the technical term for it but what happens is that the browser will update that variable with the current value because it is a reactive variable and since it is a reference being pointed to a location in memory, it will update.
If you truly wish to see the value and prove what was described above, you can write:
console.log(JSON.parse(JSON.stringify(this.state.todos)));

why does async inside computed make infinity loops (vue)?

My computed of component like this :
export default {
computed: {
dataDoctorPerPage: async function () {
const start = this.pagination.page * this.pagination.rowsPerPage - this.pagination.rowsPerPage;
const end = start + this.pagination.rowsPerPage - 1;
const doctors = this.dataDoctor
const newDoctors = {}
let key = 0
for(let item in doctors) {
if(key >= start && key <= end) {
for (let i = 0; i < doctors[item].length; i++) {
const params = {
hospitalId: doctors[item][i].hospital_id,
doctorId: doctors[item][i].doctor_id,
}
await this.getDataSchedule(params) /* call async */
// console.log(this.dataSchedule)
}
newDoctors[item] = doctors[item]
}
key++
}
return newDoctors
}
}
}
If the dataDoctorPerPage called it will run the script
await this.getDataSchedule(params) will call async/api by vuex store. My problem is there. when I call await this.getDataSchedule(params), it will loop without stopping
My vuex store like this :
const actions = {
async getDataSchedule ({ commit }, payload) {
const result = await api.getDataSchedule(payload)
const items = result.data
commit('setDataSchedule', { items: items })
},
}
How can I solve this problem?
Whether there can not run async in computed?
Computed should not use async. If you want to do that, you need another library for it. https://alligator.io/vuejs/async-computed-properties/
But what you want to do is use an asynchronous method (in the component or store) and set the data somewhere (in the component's data or store's state), then have your computed value reference that.

Vuex and dynamic states. Is possible?

I have a store in Vuex with an empty state
const state = {
data: {
}
};
And a simple mutations for test to change value or add new data
const mutations = {
changeOrAdd(state, {key,value}) {
state.data[key] = value;
}
};
If I do a commit to changeOrAdd, it is added to state BUT it doesn't have reactivity.
I did a simple trick to change a default value
const state = {
data: {
change: 0
}
};
And in the mutation:
const mutations = {
changeValueAttr(state, {
key,
value
}) {
state.data[key] = value;
state.data.change++;
}
};
And everytime I change or add a new value, it looks like a reactivity.
But, exists a way to do this without a "default" variable and without this stupid trick?
To add a new data in store vue and make it with reactivity?
Thanks
Since your keys initially are not declared in data, Vue can't track the changes. You need to use Vue.set to reactively add properties, see change detection caveats:
import Vue from 'vue'
const mutations = {
changeOrAdd(state, {key, value}) {
Vue.set(state.data, key, value)
}
}