-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RTKQ: Data stays as old value and loading immediately jumps back to false when fetching fresh data from different endpoint + query param combination #1339
Comments
Have you tried using isFetching for this? See: https://redux-toolkit.js.org/rtk-query/api/created-api/hooks#usequery
|
isFetching wouldn't be the right tool for the job. If I am loading data from an entirely different endpoint + query param combination I would want the data to be undefined so that I can show loading placeholders since no data for that query param combination has been loaded. I can't just set loading placeholders to go away if isFetching is true since that would mean anytime polling happens (user just sits on "monthly" screen with polling running for example), the data would disappear from the user while fetching the updated data. |
A temporary workaround I am using is just to set the key of the component that contains the hook to the value of the query parameter. This causes React to create an entirely new component from scratch anytime that query parameter changes. This workaround is definitely not ideal though. |
Have you also taken a look at the rest of that query result? The most obvious would be Also, take a look in the Redux Devtools and take a look what you see there. You could also export those actions and post them here. Also: |
Just checked and don't see any errors in the browser console logs or in Redux DevTools. To me this language in the docs is misleading:
To me based on the way this is worded I would expect isLoading to remain true if query param changes and the new, unrelated data is being loaded. This is also, in my opinion, desirable behavior. If the user selects "weekly", it would be inaccurate to show any data at all since that would be misleading. Typically this would go unnoticed, but the endpoint we use is obnoxiously slow AF. I attached a video of my desired behavior: data goes away entirely when the tab changes. I achieved this by doing the aforementioned component key workaround. Redux DevTools capture (sorry for zip, GitHub makes you do it for json files) Desired Behavior |
Yeah, that is a wrong wording in the docs. See it from a few other perspective:
There are plenty cases where it just makes a better user experience to keep the original data around. Even in your case, I would personally probably prefer everything going "gray" for a second instead of going UI -> nothing -> same UI with different data. If that data loads quickly, that could be some hard flickering. But of course, your We might also add an option to change this behaviour in the future, but for now it's more a reason for a docs correction ;) |
Can you give #1340 a short read-over and see if you think it explains it good for you? |
Agreed, the desired behavior definitely depends on use case. Another scenario I could imagine "reset data and isLoading when query params change" being desirable is in an e-commerce scenario. Imagine navigating from one store item page to another store item page by clicking a link in a "related items" sidebar. Technically the same component would be mounted since the router continues to show the same component. In this scenario I would expect to the old data from the old item to disappear and replaced with a loading skeleton while the new item contents load. You are definitely right about violent loading spinner/skeleton flashes being a potential issue with this particular type of behavior. To prevent this phenomenon I created a hook that returns a boolean saying whether a provided "minimum loading time threshold" has elapsed. If the minimum time hasn't elapsed, I continue to pass the components undefined instead of the fetched data so that they continue to show placeholders. Here is my example hook for that: #1276 (comment). I am very strongly convinced that other big-tech applications implement a "minimum loading time threshold" when using skeletons. For example: I have never seen the loading skeletons on the Headspace Android app flicker violently regardless of how fast my connection is. Would love to see an option you can pass the hook to control this behavior in the future. Also looked over the doc change. Much clearer. Much appreciated! |
Another point that I realized about why in certain use cases it makes sense not to show data from old args and instead make it explicitly clear that data is loading: Imagine someone using your product on an extremely low quality connection (2G or third world internet). Imagine it is bad to the point where every request takes 20 secs. Now imagine that someone clicks away from the "monthly" the "weekly" tab. Imagine that they look away for 8 secs and come back to their screen (or a colleague walks up to the screen). They will likely be under the full impression that what they are seeing in that moment is information for "weekly" despite actually being the data for "monthly". This scenario sounds comical but there is definitely something to be said about considering low quality connection scenarios and potentially misleading UI. It is personally hard for me to imagine experiencing the web with a low quality connection but there are definitely people out there that have this issue. I'd say creating a user experience that neglects poor connections or potentially misleads people with poor connection (in a general sense, not just my product scenario) is detrimental to accessability of information on the web. |
This scenario is why you should use the information |
Oh, by the way: if you want to circumvent this hook-specific behaviour: const selector = useMemo(() => api.endpoints.myEndpoint.select(arg), [arg])
const dataWithIsLoadingJustForThisCacheEntry = useSelector(selector) |
Yes using isFetching is definitely a solution. It would be really nice to differentiate initial content load from situations like polling though. Based on the situation you may want the visuals of initial content load to be different than that of polling (full loading skeleton for initial load vs tiny spinner at top right of page when polling for example). Update: saw your previous post, seems like a good temporary solution, thanks! |
I'll add an "enhancement" label on this one. Maybe we can add a switch to change behaviour or add a |
@phryneas Would this |
I just wanted to link this thread over there... 😅 |
I think the approach that React Query uses is to have an option in hook called "keepPreviousData" (https://react-query.tanstack.com/reference/useQuery) |
@phryneas @agusterodin I am in exactly the same boat and it is causing subtle problems with my UI. Say, for instance, I've built a card that displays the information for a particular item. There are 2 different use-cases desirable here: (1) refetching cases where the item's (2) refetching cases where the item's Examining the The workaround I TRIED to do: function ItemCardView({itemId}) {
const {data: _possiblyWrongData, originalArgs} = useGetItemQuery({itemId});
const correctData = originalArgs?.itemId === itemId ? _possiblyWrongData : undefined;
...
} Basically I am trying to manually overwrite Unfortunately it seems like [Not that I should have to do this hacky workaround in the first place! Default library behavior, IMHO, should be the behavior that is least likely to lead to a buggy UI. Bugs should be opt-in, not opt-out. The whole point of using this library was to reduce boilerplate for common use-cases... but if even the most common of use-cases is not supported in the normal idiomatic style without integrity bugs, I'd rather migrate to a more mature library such as React Query which apparently supports this use-case.] |
FYI... I am having to do the same hack I (attempted to) do above for the skip condition, e.g.: function DisplayList({id}) {
const {data: _possiblyIrrelevantList = []} = useGetFoobarQuery(id == null ? skipToken : {id});
const correctList = id == null ? [] : _possiblyIrrelevantList;
...
} This little hack is necessary to avoid integrity bugs literally everywhere I use RTK query with a skip token. Good library in general, but these subtle details are quite suboptimal & surprising... hopefully improvements can be made in the integrity area. |
Both of the problems I've mentioned above could be easily worked around if you just exposed one additional field, something like: Then in both cases, I could simply do: const {data: _data, argsThatQueriedCurrentData} = useGetSomethingOrOtherQuery(args);
const data = shallowEquals(args, argsThatQueriedCurrentData) ? _data : undefined; This would still be more boilerplate than I would like (as it should really not be opt-in to avoid integrity bugs), but at least this (or an equivalent solution) would be a workable solution and I wouldn't need to migrate to a different library; the situation as it currently stands is a dealbreaker. BETTER YET would be to simply do this internally yourself: function useXXXQuery(args) {
...
const result = oldImplementationOf_useXXXQuery(args);
...
if (!shallowEquals(args, argsThatQueriedCurrentData)) {
result.staleData = result.data;
result.data = undefined;
}
return result;
} Then, for 99.99999% of use-cases where I care about data integrity, I could simply do: const {data} = useGetSomethingOrOtherQuery(args); and not have to worry about bugs. If, for some reason, I really really needed to access the previous, now-incorrect data, I could then just do: const {data, staleData} = useGetSomethingOrOtherQuery(args);
const dataLackingIntegrity = data ?? staleData; Alternatively, a solution such as const {data} = useGetSomethingOrOtherQuery(args, {pleaseDoNotGiveMeBadDataIfArgsHaveChanged: true}); would work quite well and I would be quite thankful for (although it seems like this route would give you less fine-grained control).
The problem with the above statement is that |
The problem, frankly, is that none of this came up in beta and most suggestions right now either add a lot of additional api changes or would be considered breaking (moving data to So I appreciate all the comments, but this topic will need a lot of time and thought to find the best approach as to not make things worse. So this will stay open, but probably not resolved in the near future. And please keep in mind when writing something like
React Query is over two years old and the author of React Query does Open Source as his job while we are doing this in addition to our jobs. It would be pretty hard to live from the 45$ of GitHub sponsors I'm getting out of this, so there is limited time and a lot of stuff to do. |
@phryneas sorry, didn't mean to send any discouragement your way. Hopefully my comments at least gave some food for thought. But now I feel bad so I will donate lol. |
@HansBrende Please don't just because you feel bad ;) |
@phryneas too late lol |
@HansBrende Whoa, thanks a lot! |
@phryneas recognizing that you have constrained development resources/time & want to avoid backwards-incompatible changes, I will try to boil all of my comments down to the shortest backwards-compatible path where I become unblocked. I can potentially implement workarounds in my own codebase to cover the rest, but there are some practical blockers from doing so at the moment. If any ONE of these blockers are alleviated, I'd become unblocked from implementing a workaround (even if it is a painful/hacky workaround): Blocker
|
@HansBrende while 3. is something we should probably do either way (good catch!) I think all these are just really workarounds. I think #1500 might be a good solution, just also exposing the " @agusterodin @pserrer1 Could everyone that has commented in this issue please try that out and leave their feedback? Is this exactly what you need? Just kinda? Does it leave problems or questions open? You can find easy installation instructions for the CI build on the CodeSandbox build page. |
Awesome, thanks! Will try out and report back by end of day. It should coexist fine with selectFromResult, right? Edit: just left feedback on the PR. Fixes the problem I was having and I like the approach. |
@phryneas hi, you say demo link: |
@zousandian that does indeed look like a bug. I've opened #1519 to track it. |
This was closed with multiple PRs but I'm wondering if there's an official solution is to the original problem. We're using RTK query on a dashboard application that has a bunch of cards and the queries for all the cards use polling. We want to show spinners on the initial load, but not on subsequent polled requests, so To solve this right now, for every single query we have an extra piece of state and two useEffects to support that use case: const [customIsLoading, setCustomIsLoading] = useState(true);
// set customIsLoading flag to true when user changes the input to the dashboard
useEffect(() => setCustomIsLoading(true), [dashboardInput])
// set customIsLoading to false if the query isn't fetching
useEffect(() => {
if (!queryResult.isFetching) {
setCustomIsLoading(false);
}
}, [queryResult.isFetching]) This essentially gives the effect of |
Hey, I am experiencing an issue when using the useQuery auto-generated hook. If I change query parameters, the data remains the same (I expect it to be undefined so I can show loading placeholder) and loading jumps from "true" immediately back to "false" despite the data for that given combination not being loaded yet. I have keepUnusedDataFor set to 0 btw (not sure if relevant or not).
The docs claim:
This is really hard to reproduce in a Codepen since it relies on the server having some sort of delay. I wasn't aware of this bug until I used an endpoint that is extremely slow (4+ sec response time).
Hopefully this attached video, code snippet, and console log demonstrate the issue effectively. Please let me know if there are any other details I can provide!!
Notice that despite the argument changing from "monthly" to "weekly" it is still providing the "monthly" data instead of data being undefined while still loading.
Component
Recreation In Browser
https://user-images.githubusercontent.com/10248395/126681813-9656f46e-46a8-4a2e-b316-9229291699b5.mov
The text was updated successfully, but these errors were encountered: