VueJS data doesnt change on URL change - vue.js

My problem is that when I go from one user page to another user page the info in component still remains from first user. So if I go from /user/username1 to /user/username2 info remains from username1. How can I fix this ? This is my code:
UserProfile.vue
mounted() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
And this is from actions.js file to get user:
const getUserProfile = async ({
commit
}) => {
try {
const response = await API.get('/user/' + router.currentRoute.params.username);
if (response.status === 200 && response.data.user) {
const data = {
success: true,
user: response.data.user,
invites: response.data.invites
}
return data;
} else {
return console.log('Something went wrong.');
}
} catch (error) {
console.log(error);
}
};
Should I add watch maybe instead of mounted to keep track of username change in url ?

You can use watch with the immediate property, you can then remove the code in mounted as the watch handler will be called instead.
watch: {
'$route.params.username': {
handler: function() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
deep: true,
immediate: true,
},
}

Your page is loaded before the data is retrieved it seems, you need put a "loading" property in the data and have a v-if="!loading" for your component then it will only render once the display is updated. Personally I would avoid watch if I can it is not great for performance of for fine grained handling.

Yes you should add wach on statement that contain user info.(you may have a problem to watch on object, so you can save user info in json, but im not sure). When user changing - call action, after recived response call mutation that should change a state, then watch this state.
And you might use better syntax to receive data from store. That is really bad idea call dispatch directly from your mouted hook, use vuex documentation to make your code better.

Related

Vuex Getter not pulling data

I have a vuex store that I am pulling data from into a component. When the page loads the first time, everything behaves as expected. Yay.
When I refresh the page data is wiped from the store as expected and pulled again into the store as designed. I have verified this is the case monitoring the state using Vuex dev tools. My getter however doesn't pull the data this time into the component. I have tried so many things, read the documentation, etc and I am stuck.
Currently I am thinking it might be an issue with the argument?...
If I change the argument in the getter, 'this.id' to an actual value (leaving the dispatch alone - no changes there), the getter pulls the data from the store. So it seems the prop, this.id has the correct data as the dispatch statement works just fine. So why then wouldn't the getter work?
this.id source - The header includes a search for the person and passes the id of the person that is selected as the id prop. example data: playerId: 60
Thoughts? Appreciate any help.
This code works on initial page load, but not on page refresh.
props: ["id"],
methods: {
fetchStats() {
this.$store.dispatch("player/fetchPlayer", this.id).then(() => {
// alert(this.id);
this.player = this.$store.getters["player/getPlayerById"](this.id);
this.loading = false;
});
}
},
This code (only changing this.id to '6' on getter) works both on initial load and page refresh.
props: ["id"],
methods: {
fetchStats() {
this.$store.dispatch("player/fetchPlayer", this.id).then(() => {
// alert(this.id);
this.player = this.$store.getters["player/getPlayerById"](6);
this.loading = false;
});
}
},
Here is the getPlayerById getter:
getPlayerById: state => id => {
return state.players.find(plr => plr.playerId === id);
},
Here is the fetchPlayer action:
export const actions = {
fetchPlayer({ state, commit, getters }, id) {
// If the player being searched for is already in players array, no other data to get, exit
if (getters.getIndexByPlayerId(id) != -1) {
return;
}
// If the promise is set another request is already getting the data. return the first requests promise and exit
if (state.promise) {
return state.promise;
}
//We need to fetch data on current player
var promise = EventService.getPlayer(id)
.then(response => {
commit("ADD_PLAYER", response.data);
commit("CLEAR_PROMISE", null);
})
.catch(error => {
console.log("There was an error:", error.response);
commit("CLEAR_PROMISE", null);
});
//While data is being async gathered via Axios we set this so that subsequent requests will exit above before trying to fetch data multiple times
commit("SET_PROMISE", promise);
return promise;
}
};
and mutations:
export const mutations = {
ADD_PLAYER(state, player) {
state.players.push(player[0]);
},
SET_PROMISE(state, data) {
state.promise = data;
},
CLEAR_PROMISE(state, data) {
state.promise = data;
}
};

I'm getting this problem in vue trying to display a graphic with apexcharts?

I work with vue and apexchart. I call the api with the async method, to make sure that I am showing the correct information. I'm using console.Log and the array is correct. So does anyone know what is happening here? Thanks.
Probably your graph is created before the data of your api arrives. Try this :
data: () => ({
series: null
options: { ... }
}),
async mounted() {
this.loaded = false;
try {
const res = await fetch(urlYandex);
const json = await res.json()
this.series = json.data[0].metrics[0];
this.loaded = true;
} catch (e) {
console.error(e);
}
},
your apexchart you add like this :
<apexchart :options="options" :series="series" v-if="loaded"></apexchart>

Which Lifecycle hook after axios get but before DOM render

I'm trying to render my DOM, dependent on some data I'm returning from an axios get. I can't seem to get the timing right. The get is in the created hook, but there is a delay between the get and actually receiving the data. Basically if there is info in seller_id then I need to show the cancel button, otherwise don't. Here is my code:
this is in my created hook
axios.get('https://bc-ship.c9users.io/return_credentials').then(response => {
this.seller_id = response.data.seller_id;
this.selected_marketplace = response.data.marketplace;
this.token = response.data.auth_token;
});
and then this is the logic to show or hide the button. I've tried created, mounted, beforeUpdate, and updated all with no luck. I've also tried $nextTick but I can't get the timing correct. This is what I have currently:
beforeUpdate: function () {
// this.$nextTick(function () {
function sellerIdNotBlank() {
var valid = this.seller_id == '';
return !valid;
}
if(sellerIdNotBlank()){
this.show_cancel_button = true;
}
// })
},
First, it is pointless to get your data from backend and try to sync with Vue.js lifecycle methods. It never works.
Also, you should avoid beforeUpdate lifecycle event. It is often a code smell. beforeUpdate is to be used only when you have some DOM manipulations done manually and you need to adjust them again before Vue.js attempt to re-render.
Further, show_cancel_button is a very good candidate for a computed property. Here is how component will look:
const componentOpts = {
data() {
return {
seller_id: '',
// ... some more fields
};
},
created() {
axios.get('https://bc-ship.c9users.io/return_credentials').then(response => {
this.seller_id = response.data.seller_id;
this.selected_marketplace = response.data.marketplace;
this.token = response.data.auth_token;
});
},
computed: {
show_cancel_button() {
return this.seller_id !== '';
}
}
}

"Simulate" mutations in vuex

import { remoteSettings } from 'somewhere';
const store = {
state: {
view: {
foo: true
}
},
mutations: {
toggleFoo(state) {
state.view.foo = !state.view.foo;
}
},
actions: {
async toggleFoo({ state, commit }) {
commit('toggleFoo');
await remoteSettings.save(state);
}
}
};
Say I have a simple store like this. toggleFoo action applies the mutation, then saves the new state by making an async call. However, if remoteSettings.save() call fails, local setting I have in the store and remote settings are out of sync. What I really want to achieve in this action is something like this:
async toggleFoo({ state, commit }) {
const newState = simulateCommit('toggleFoo');
await remoteSettings.save(newState);
commit('toggleFoo');
}
I'd like to get the new state without actually committing it. If remote call succeeds, then I'll actually update the store. If not, it's going to stay as it is.
What's the best way to achieve this (without actually duplicating the logic in the mutation function)? Maybe "undo"? I'm not sure.
One way of doing this would be: (credit to #Bert for correcting mistakes)
Store the old state using const oldState = state; before committing the mutation.
Wrap the async call in a try-catch block.
If the remoteSettings fails it will pass the execution to catch block.
In the catch block commit a mutation to reset the state.
Example:
const store = {
state: {
view: {
foo: true
}
},
mutations: {
toggleFoo(state) {
state.view.foo = !state.view.foo;
},
resetState(state, oldState){
//state = oldState; do not do this
//use store's instance method replaceState method to replace rootState
//see : https://vuex.vuejs.org/en/api.html
this.replaceState(oldState)
}
},
actions: {
async toggleFoo({ state, commit }) {
const oldState = JSON.parse(JSON.stringify(state)); //making a deep copy of the state object
commit('toggleFoo');
try {
await remoteSettings.save(newState);
//commit('toggleFoo'); no need to call this since mutation already commited
} catch(err) {
//remoteSettings failed
commit('resetState', oldState)
}
}
}
};
Borrowing code from #VamsiKrishna (thank you), I suggest an alternative. In my opinion, you want to send the changes to the server, and update the local state on success. Here is a working example.
To prevent duplicating logic, abstract the change into a function.
console.clear()
const remoteSettings = {
save(state){
return new Promise((resolve, reject) => setTimeout(() => reject("Server rejected the update!"), 1000))
}
}
function updateFoo(state){
state.view.foo = !state.view.foo
}
const store = new Vuex.Store({
state: {
view: {
foo: true
}
},
mutations: {
toggleFoo(state) {
updateFoo(state)
},
},
actions: {
async toggleFoo({ state, commit }) {
// Make a copy of the state. This simply uses JSON stringify/parse
// but any technique/library for deep copy will do. Honestly, I don't
// think you would be sending the *entire* state, but rather only
// what you want to change
const oldState = JSON.parse(JSON.stringify(state))
// update the copy
updateFoo(oldState)
try {
// Attempt to save
await remoteSettings.save(oldState);
// Only commit locally if the server OKs the change
commit('toggleFoo');
} catch(err) {
// Otherwise, notify the user the change wasn't allowed
console.log("Notify the user in some way that the update failed", err)
}
}
}
})
new Vue({
el: "#app",
store,
computed:{
foo(){
return this.$store.state.view.foo
}
},
mounted(){
setTimeout(() => this.$store.dispatch("toggleFoo"), 1000)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.9/vue.js"></script>
<div id="app">
<h4>This value never changes, because the server rejects the change</h4>
{{foo}}
</div>

Get item from AsyncStorage in React Native

I have a list of companies in React Native.
When I click on one of those companies I get the url of the API that is used for selected company. Then I store it to AsyncStorage and then I show the login screen. The function is as follows:
selectCompany(data_url, e) {
AsyncStorage.setItem("data_url", JSON.stringify(data_url), () => this.props.login());
}
Then on login page if I click on sign in button I go to the onLogin function, the function is as follows:
onLogin: function() {
fetch(data.url + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
},
And data.url comes from data.js file, and I try to get url from the data.js file as follows:
let data_url = AsyncStorage.getItem("data_url").then(json => JSON.parse(json));
module.exports = {
url: data_url,
.....
}
But it doesn't work. Any advice?
AsyncStorage is async, therefore data_url will not be defined until it's retrieved what its looking for, you would need to move the fetch into the promise thats returned from the get so it will run it once it's done getting the data. This might be one way you tackle it:
const data_url = () => AsyncStorage.getItem("data_url"); //change this into a function
module.exports = {
url: data_url,
.....
}
now inside your component...
onLogin: function() {
data.url().then((url) => {
fetch(JSON.parse(url) + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
});
},
AsyncStorage.getItem is a promise and needs to await for response rather than accessing direct and the function calling it should be defined as async. Here is an example to retrieve from AsyncStorage..
export async function getAccessKey(){
let accessToken = await AsyncStorage.getItem(ACCESS_TOKEN);
return accessToken;
}