Expo, React Native: Data still stale after mutation and query invalidation.
Situation: I mutate data displayed on the current screen and then invalidate queries in the 'onSettled' or the 'onSuccess' section.
Problem: Stale data still displayed on the current screen, unless i navigate away from the page and come back.
const { data, isLoading, isError, refetch } = useQuery(
'getAllCategories',
getAllCategories,
{
onSuccess: (data) => {
setSearchResults(Object.values(data));
},
}
);
const deleteCategoryMutation = useMutation(
(id) => {
deleteCategory({ categoryID: id, saveItems: true });
},
{
onSettled: async () => {
await queryClient.invalidateQueries(['getAllCategories']);
refetch();
},
}
);
Related
const [dataSortArray, setDataSortArray] = useState([]);
// seeAllFeeds
const { data, loading, refetch, fetchMore } = useQuery(SEE_ALL_FEEDS_QUERY, {
variables: {
offset: 0,
},
});
useEffect(() => {
if (!loading) {
refetch();
console.log(data);
setDataSortArray(data);
console.log("✅", dataSortArray);
}
}, []);
as you can see I use useQuery.
if loading is done, i refetch the query and console.log(data) then data contains some array.
and I put this array into dataSortArray using setDataSortArray.
but when I console.log dataSortArray, it's empty.
✅ Array []
do you know why?
put loading into dependency array it will invoke every time value of loading is change passing Empty array [] as dependency array means it will render only once
useEffect(() => {
if (!loading) {
refetch();
console.log(data);
setDataSortArray(data);
console.log("✅", dataSortArray);
}
}, [loading]);
As #Dominic have pointed out in the question's comments (with a relevant link to another SO thread), useState is not effective immediately. So if you call setState on varA and console.log immediately afterwards, the varA won't be in the latest state you would expect.
I would suggest handling the state of dataSortArray in a separate useEffect.
useEffect(() => {
if (!loading) {
refetch();
console.log(data);
setDataSortArray(data);
}
}, []);
// Log dataSortArray when processing for setDataSortArray is completed by React.
useEffect(() => {
console.log(dataSortArray)
}, [dataSortArray]}
setup(){
const columns = computed(()=>store.state['subCategory'].subCategoryColumnsData[subCategoryName.value]);
const { fetch } = useFetch(async () => {
await store.dispatch('subCategory/getColumnsQuery', {
categories: subCategoryId.value,
page: 1,
subCategoryName: subCategoryName.value,
})
});
fetch();
}
I want to switch between pages in my project. Whenever I switched another page, I send request to get data with latest updates. This code works well for the first time when page was loaded, but it doesn't work when I switched from one page to another page. But if I check store state, I can see it in store. If I visit same page second time , I can see data this time.
But if I change my code like this, it works well. I did not get why it does not work true in the first sample
setup(){
const columns = ref([])
const { fetch } = useFetch(async () => {
await store.dispatch('subCategory/getColumnsQuery', {
categories: subCategoryId.value,
page: 1,
subCategoryName: subCategoryName.value,
})
}).then(() => (columns.value = store.state['subCategory'].subCategoryColumnsData[subCategoryName.value]));
fetch();
}
Can you test it? sample:
const state = reactive({ columns: computed(() => yourstore })
// do not need to call fetch because this hook is a function
useFetch(async () => { await store.dispatch(url) })
return {
...toRefs(state),
}
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 have a useEffect function where a redux action is called and data is written to prop. My Problem is that useEffect loop many times and flooded the server with requests.
const { loescherData, navigation } = props;
useEffect(() => {
AsyncStorage.getItem('userdata').then((userdata) => {
if (userdata) {
console.log(new Date());
console.log(userdata);
var user = JSON.parse(userdata);
props.fetchLoescherDetails(user.standort);
setData(props.loescherData);
}
});
}, [loescherData]);
if i leave it blank the rendering is finished before receiving data and the content would not updated.
is there another way to work with this function?
loescherData won't be available right after calling your redux-action fetchLoescherDetails ... and changing component by setData will cause an infinite rendering cause your current useEffect has a dependency on loescherData
So I'd suggest you exec your redux-action onComponentDidMount by passing an empty-deps [] to your effect ... and then consume the output of you action in a different effect
useEffect(() => {
AsyncStorage.getItem('userdata').then((userdata) => {
if (userdata) {
console.log(new Date());
console.log(userdata);
var user = JSON.parse(userdata);
props.fetchLoescherDetails(user.standort);
// setData(props.loescherData);
}
});
}, []);
useEffect(() => {
if (loescherData) {
// do some with loescherData like setState
}
}, [loescherData]);
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.