React Query invalidateQueries updating data after post - reactjs

I'm working on a simple React CRUD with React Query, and I've being stuck for a while trying to update my fetched data after I post a new item with invalidateQueries but it doesn't work, being trying many things like: onSuccess, onMutate, onSettled, etc etc, and nothing seems to work, also following the React Query Documentation. Here is my code:
const queryClient = useQueryClient();
const handleSubmitButtonNew = async (data) => {
api.createAnnouncements(data);
};
const mutation = useMutation(handleSubmitButtonNew, {
onSuccess: () => {
queryClient.invalidateQueries("Announcements");
},
});
const { data, status, isLoading } = useQuery(
"Announcements",
api.getAnnouncements
);
and then in a child component I call:
mutation.mutate({ title: title.value, content: content.value, user });
Can I call the queryClient.invalidateQueries("Announcements"); from another component where the "Announcements" originally is fetching?
What I'm doing wrong ?

Related

How to make manual data validation/mutation work with useSWR and Axios?

I have tried everything I could so far, but can't get to make this manual data revalidation/mutation to work using const { mutate } = useSWRConfig().
I have tried setting GlobalConfig on my app, and not, but to no changes.
For example: In my "AddressesList" component, I fetch addresses list using the hook:
const { data, isValidating, error} = useSWR('v1/addresses', fetchMyAddresses, {
revalidateOnFocus: false,
revalidateIfStale: false,
});
where the fetcher is a simple is a simple fetchMyAddresses = () => axiosApiInstance.get('v1/addresses').then(res => res.data);
Then in another component, after creating a new address, I use:
const { mutate } = useSWRConfig();
...
await createNewAddress();
if (success) {
mutate('v1/addresses');
redirect('AddressesList');
}
and on success, I am redirected to the AddressesList. But my data list is not updated with the new address... how is that supposed to work?

refetch in reactQuery is not return the data

I am using reactQuery in my react application. I need to call one get API in button click. for that i am using refetch option in reactQuery. API call is working fine but my response data is coming undefined. I checked in browser network there i can see the response.
My API call using reactQuery
const { data: roles, refetch: roleRefetch } = useQuery('getRoles', () => api.getRoles('ID_234'), { enabled: false });
My click event
const handleAdd = (e) => { roleRefetch(); console.log(roles) }
My action call using axios
export const getRoles = (name) => axios.get(roles/list?sa_id=${name}, { headers: setHeader }).then(res => res);
const handleAdd = (e) => { roleRefetch(); console.log(roles) }
this not how react works, and it's not react-query specific. calling a function that updates some state will not have your state be available in the next line. It will make it available in the next render cycle. Conceptually, you want this to work, which cannot with how react is designed:
const [state, setState] = React.useState(0)
<button onClick={() => {
setState(1)
console.log(state)
}}
here, the log statement will log 0, not 1, because the update doesn't happen immediately, and this is totally expected.
With react-query, what you can do is await the refetch, because its async, and it will give you the result back:
const handleAdd = async (e) => {
const { data } = await roleRefetch();
console.log(data)
}
or, depending on what you actually want to do, you can:
use data in the render function to render something - it will always be up-to-date.
use theonSuccess callback of useQuery to trigger side-effects whenever data is fetched
spawn a useEffect in the render function that does the logging:
const { data: roles, refetch: roleRefetch } = useQuery('getRoles', () => api.getRoles('ID_234'), { enabled: false });
React.useEffect(() => {
console.log(roles)
}, [roles])
on a more general note, I think disabling a query and then calling refetch on a button click is very likely not idiomatic react-query. Usually, you have some local state that drives the query. in your case, that's likely the id. Dependencies of the query should go to the queryKey, and react-query will trigger a refetch automatically when the key changes. This will also give you caching by id. You can use enabled to defer querying when your dependencies are not yet ready. Here's what I would likely do:
const [id, setId] = React.useState(undefined)
const { data: roles } = useQuery(['getRoles', id], () => api.getRoles(id), { enabled: !!id });
const handleAdd = (e) => { setId('ID_234') }
of course, id doesn't have to come from local state - it could be some other form of client state as well, e.g. a more global one.

Redux toolkit RTK query mutation not getting returning data

Hi I recently learned the new react toolkit with the rtk query tool, and I am trying to put in a login system together using the createApi from the rtk package.
After giving it a test on the login button pressed, I see the network request going through without any issue(status code 200), and I get a response object providing user, token, however, when I try to get the returning data using useLoginMutation I get an undefined value.
below is the code for my endpoint which is injected in a base api:
export const apiLogin = theiaBaseApi.injectEndpoints({
endpoints: (build) => ({
loginUser: build.mutation<UserReadonly, loginValuesType | string>({
query: (values: loginValuesType, redirect?: string) => {
const { username, password } = values;
const header = gettingSomeHeaderHere
return {
url: "login",
method: "GET",
headers,
crossDomain: true,
responseType: "json",
};
},
}),
}),
});
export const { useLoginUserMutation } = apiLogin
then inside my React component I destructure the mutation result such like below:
const [login, {data, isLoading}] = useLoginUserMutation();
const submitLogin = () => {
// pass in username password from the form
login({username, password});
}
Suppose if I console log out data and isLoading I assume that I will see data: {user: "abc", token: "xyz"}, because under network tab of my inspect window I can see the response of this network request, but instead I am seeing data: undefined
Does any have experience on solving this?
Oh I found the reason, it was a very careless mistake. I had to wrap the reducer to my store, which was what I was missing
In my case the issue was that I was trying to access the UseMutationResult object inside onClick callback. And the object was not updating inside the callback, even though in the component the values were accurate.
If I put the log outside it's working just fine.
here is an example for better understanding (inside handleAddPost the mutationResult is not updating)
Here is a code sample (in case link is not working):
const Component = () => {
const [addPost, mutationResult] = useAddPostMutation();
...
const handleAddPost = async () => {
...
console.log("INSIDE CALLBACK isLoading and other data is not updating:");
console.log(JSON.parse(JSON.stringify(mutationResult)))
...
};
// in the example this is wrapped in an useEffect to limit the number of logs
console.log(mutationResult.data,"OUTSIDE CALLBACK isLoading and other data is working:")
console.log(JSON.parse(JSON.stringify(mutationResult)))
return (
...
<Button
...
onClick={handleAddPost}
>
Add Post
</Button>
...

useSWR integration with pagination on backend

When page changes, new query is created and it's data is set to initialData.
In this case user sees initialData before new query data is fetched:
import React from "react";
import fetch from "../lib/fetch";
import useSWR from "swr";
function Component({ initialData }) {
const [page, setPage] = React.useState(1);
const { data } = useSWR(
`/api/data?page=${page}`,
{ initialData }
);
// ...
}
Component.getServerSideProps = async () => {
const data = await fetch('/api/data?page=1');
return { initialData: data };
};
My issue is that initialData is used as a fallback every time I query for new data:
Do you have any ideas on how I can prevent this flicker?
So in react-query, I think there are multiple ways to avoid this:
keepPreviousData: true
this is the main way how to do pagination, also reflected in the docs as well as the examples.
It will make sure that when a query key changes, the data from the previous queryKey is kept on the screen while the new fetch is taking place. The resulting object will have an isPreviousData flag set to true, so that you can e.g. disable the next button or show a little loading spinner next to it while the transition is happening. It is similar in ux what react suspense is going to give us (somewhen).
initialData function
initialData accepts a function as well, and you can return undefined if you don't want initial data to be present. You could tie this to your page being the first page for example:
function Component({ initialData }) {
const [page, setPage] = React.useState(1);
const { data } = useQuery(
['page', id],
() => fetchPage(id),
{ initialData: () => page === 1 ? initialData : undefined }
);
}
keep in mind that you will have a loading spinner then while transitioning, so I think this is worse than approach 1.
since your initialData comes from the server, you can try hydrating the whole queryClient. See the docs on SSR.
initialData works very well with keepPreviousData, so here is a codesandbox fork of the example from the doc where initialData is used (solution 1). I think this is the best take: https://codesandbox.io/s/happy-murdock-tz22o?file=/pages/index.js

Issue with refetching specific GraphQL obvervable query

I'm very new to React and GraphQL(Apollo) and learning the stack.
I have a React Component that loads some page data and there is a "Reload" button on the page.
Problem:
I want to refetch the data when I click on "Reload" button.
However, I could not find a way to refetch the data from queryObservable.
How do I refetch the data from QueryObservable? Is there anyway to call refetch?
I tried using reFetchObservableQueries() however, this reloads the page and refetches everything. Can I specifically refetch only a single observable?
This is my code:
getPageQueryObservable = (contentId, key, paginated = false) => {
const query = contentId ? TreeQuery : RootLevelQuery;
const defaultVariables = 'Test';
const variablesForPagination = {
paginationLimit: INITIAL_PAGE_SIZE,
...defaultVariables
};
return this.props.apolloClient.watchQuery({
query,
variables: paginated === true ? variablesForPagination : defaultVariables
});
};
loadPages = async (paginated = false) => {
const { pages: localPages } = this.state;
const { id, key } = this.props;
const queryObservable = this.getPageQueryObservable(
id,
key,
paginated
);
// I want to ideally call this stuff again..When "Reload" button is clicked.
this.querySubscription = queryObservable.subscribe({
next: ({ data, loading }) => {
....
});
You can call query with the same query and variables but a fetchPolicy of network-only in order to refetch the query. This should update the cache and therefore any relevant Observables.
await client.query({ query, variables, fetchPolicy: 'network-only' })
However, there's really no reason to use watchQuery directly. Instead, you should use the hook API to fetch and refetch your data.
const { data, loading, refetch } = useQuery(query, { variables })
Not only does this reduce boilerplate, but now refetching is as simple as
await refetch()

Resources