Open RareSecond opened 2 months ago
Hi @neehhaa06
While it shouldn't matter, I've tried your suggestion, but nothing changed.
Having the exact same issue. With me it happen when I quickly redirect to another page but useQuery started already, leaving it in a state of always saying isLoading and never updating or being able to refetch
This stripped down version allowed me to continually reproduce this. (have not tested if this actually works with the stripped down code)
The component causing a redirect
const Checkout = (): ReactElement => {
const { t } = useTranslation();
const { cart, syncCart } = useCartStore();
const [loading, setLoading] = useState<boolean>(true);
useEffect(() => {
if (cart && cart.lineItems.length === 0) {
redirect('/cart');
}
setLoading(false);
}, [cart]);
if (loading) {
return <></>;
}
return (
<div className="container mb-10">
<div className="flex flex-col lg:flex-row gap-8 mt-8">
<PaymentMethods />
</div>
</div>
);
};
The useQuery being used where getPaymentMethods
is the server action:
const usePaymentMethods = (): UseQueryResult<PaymentMethod[]> => {
const { shop } = useShopContext();
return useQuery<PaymentMethod[]>({
queryKey: ['paymentMethods', shop],
queryFn: async () => {
return await getPaymentMethods();
},
});
};
export default usePaymentMethods;
Inside PaymentMethod the useQuery is initiated due to it being rendered into the DOM, but the redirect quickly interrupts that call, causing it to be in a constant loading state.
The workaround for this is to not use server actions, but to create an API proxy yourself and there running the action. Not the most ideal fix ofcourse since it just shouldn't happen.
I'm facing a similar issue. Few actions are called, but some are pending in an infinite isPending
state, and the server action is never called.
It only runs all actions then on full page refresh.
Is there going to be any fix for this, as this is a major issue?
Not really sure how to address this tbh. If the logs are correct and you see "About to get roles"
, it means the queryFn
is executing and we do nothing until the queryFn
finishes. If the next thing is calling the server action, then I don't see why this wouldn't happen.
That said, it's not recommended (neither from react-query side nor from nextjs side) to actually use serverACTIONS for something that aren't "actions" (things that have side-effects, we also call them mutations
here).
Right now, serverActions run in serial. All of them. One by one. So if you have two unrelated queries, they will be queued up. Maybe the flakiness has to do with how cypress performs tests in parallel and they get queued, I really don't know.
I know that everyone sees the benefit of not having to write API routes because server actions are basically RPC calls but this is not what they are meant for. trpc
is still pretty good for this with nextJs.
@TkDodo This is coming straight from the Next.js docs
Server Actions are not limited to
<form>
and can be invoked from event handlers, useEffect, third-party libraries, and other form elements like<button>
.
This isn't a mutation
in this case, since we are just fetching the data.
I want to again stress the fact that, when using a simple useEffect
and (very high level, I know) recreating the behavior of react-query
, everything works as expected.
I know this one's probably a massive bitch to debug, but I do think this is something with react-query
that's going wrong. Would you want me to try swr
and see if it suffers from the same issue?
This is coming straight from the Next.js docs
yes, but it's still an "action". The section in the docs is called Server Actions and Mutations
, and it's next to Data Fetching and Caching
. Using a server action for data fetching is, again, not recommended by next or by us.
I talked to the nextjs team not too long ago and mid-term, they want to provide something like "server actions" but for fetching. But server actions ain't it.
I want to again stress the fact that, when using a simple useEffect and (very high level, I know) recreating the behavior of react-query, everything works as expected.
I hope it's understandable that my answer here can only be:
I want to again stress the fact that, when using a simple fetch
and (very high level, I know) recreating the behavior of server actions, everything works as expected.
Bottom line is I really don't even know how to look into this ...
Right now, serverActions run in serial. All of them. One by one. So if you have two unrelated queries, they will be queued up.
This surprised me. I tested this and it seems that it is incorrect, unless I'm missing something - server actions can run in parallel: See this Codesandbox.
@bartcheers your sandbox just calls an async function (that is marked as "use server") in a server component.
To really get a server action, that gets transformed to a POST request under the hood, you need to invoke the action from a client component (e.g. in a button click handler).
Thanks @TkDodo for clarifying. Here's another Codesandbox confirming server actions run in series, not in parallel. Great to know!
I think we should just amend the docs that it's not recommended to use server actions with useQuery
and that API routes are still the way to go. Would someone want to do that?
While I'm starting to see more why we shouldn't use it for data fetching, I do still think that there's an issue on your part.
I recreated the test in Playwright and ran it 100 times (for 3 different browsers, so 300 tests in total).
This was React Query
This was swr
We will be making the switch to swr, as that's an easier replacement than making the switch to API routes/tRPC.
I've hit this as well on one of my personal projects. wierdly only happening in dev. Using server actions to fetch data client side in combination with react-query and all hell breaks loose...
const { hasNextPage, isFetchingNextPage, fetchNextPage, data, isLoading } =
useInfiniteQuery({
queryKey: key,
queryFn: ({ pageParam }) => {
const offset = (pageParam - 1) * pageSize;
return query(offset);
},
refetchOnWindowFocus: false,
initialPageParam: 1,
getNextPageParam: (lastPage, pages) => {
if (lastPage.length === 0) return;
return pages.length + 1;
},
});
lastPage
would intermittently be null 🤷
I seem to be experiencing this problem in production, but with useMutation invoking a server action:
export function useUpdateProduct() {
return useMutation({
mutationFn: updateProduct, // this is a server action
onSuccess: () => {
toast.success('Your product has been saved.');
},
onError: (e) => {
console.error(e);
toast.error('Failed to save your product. Please try again.');
},
});
}
Once this problem occurs, this and other mutations fail without even invoking the server action. Once I refresh the page they start working again.
Describe the bug
Please bear with me, because I have no idea how to best start. I've also done my best to narrow this down as much as possible, but I haven't been able to provide a minimal reproducible example.
Setting the stage
We were noticing more and more lately that our (Cypress) tests are flakey so I started to investigate it. I've managed to track it down to a custom hook not resolving with the correct data.
The hook,
useCurrentUser
, is in essence a very simple hookgetCurrentUserWithRoles
is also a very simple server action that does some database fetching.However, when running
cypress open
and running a test that relies on theuseCurrentUser
returning correct information, the hook sometimes fails to return any data.What I've tried
Adding logs
If I add logs to the necessary places, I can see where it goes wrong
The first log gets printed, but the other two don't appear. So for some reason, it seems that it never reaches the server action.
Removing React Query
If I remove React Query and just rely on
useEffect
anduseState
, there's no flakiness and test passes 100% of the timeReplacing the server action with an actual API call
I also went ahead and created an actual API call (via
route.ts
) where I returned hardcoded data. And again, it works 100% of the time.Replacing Cypress with Playwright
I even rewrote the test in Playwright but to no avail. The tests are still just as flaky, feeling even worse (but that may be my mental state acting up by now).
Some extra insights
However, the switch to Playwright has brought some extra insights that may be helpful
chromium
,firefox
andwebkit
), the tests always pass forchromium
andfirefox
, and fail way less forwebkit
(10% of the times instead of 70%)workers
to 1 in PlaywrightYour minimal, reproducible example
I tried, but can't condense it enough to have it always fail
Steps to reproduce
/
Expected behavior
I expect React Query to actually fire the Next.js server action and return the data
How often does this bug happen?
Often
Screenshots or Videos
No response
Platform
Tanstack Query adapter
react-query
TanStack Query version
v5.52.0
TypeScript version
v5.3.3
Additional context
Please let me know what other information I can provide. I felt like I've been going down the rabbit hole, with no solution in sight..