0

I have a query that uses placeholderData: keepPreviousData. This means the query is in a hard loading state, with no placeholder data, on initial mount. Then, when the queryKey changes, it goes into a 'soft' loading state: placeholder data shown, isPlaceholderData=true and isLoading=false.

My component looks like this:

const exampleQ = useQuery({
  queryKey: ['example'],
  queryFn: example,
  placeholderData: keepPreviousData,
});

if (exampleQ.isLoading) {
  return (
    <div>
      hard loading state, no placeholder data to display. big spinner here.
    </div>
  );
} else if (exampleQ.isSuccess) {
   return (
     // Show outdated data with overlay if isPlaceholderData. Otherwise just display the data.
     // If a background refresh (isRefetching) is happening, we show the
     // current data with no indication to the user.
     <div className={exampleQ.isPlaceholderData ? 'overlay' : ''}>
       Data: { exampleQ.data }
     </div>
  );
}

Now, somewhere far away in the component tree, I need to put exampleQ back into the soft loading state, with isLoading=false, isPlaceholderData=true. I then wait some time for something unrelated. When ready, I want the query to refetch and continue to display the placeholderData while refetching.

const queryClient = useQueryClient()

async function onUpdate() {
  queryClient.putBackToSoftLoading(['example']);  // trigger soft loading, does this method exist?
  await doSomething();  // async waiting. overlay and keepPreviousData showing during this time.
  queryClient.triggerFetchWhileKeepingPlaceholder(['example'])  // trigger refetch whie staying in soft loading.
}

I've tried every combination of QueryClient methods that I can think of and none of them put the query back to the soft loading state in a way that I can tell the difference between that and background refreshes. What is the right way to accomplish this? Am I going about this the wrong way, and there's some other React way that I should be communicating between this components other than react-query?

  • resetQueries goes back to hard loading instead of soft, and also sends out the request before I'm ready.
  • refetchQueries doesn't set isPlaceholderData=true so my component can't tell if its background refetching or or not.
  • fetchQuery gives me no way to set isPlaceholderData=true

1 Answer 1

0

This doesn’t work because placeholderData is a feature of the useQuery “instance” - specifically, the QueryObserver that is created by useQuery. It is only available on this instance.

Also, your example is incomplete, because with a static queryKey, you would never get previousData because “previous” refers to a previous queryKey being observed.

So more likely, it’s something like this:

const exampleQ = useQuery({
  queryKey: ['example', someDynamicData],
  queryFn: example,
  placeholderData: keepPreviousData,
});

Now when this instance is mounted and someDynamicData changes between values while you already have data for a previously observed someDynamicData, you will keep seeing that data as “placeholder” until the new query finishes loading.

So what you likely need to do is change someDynamicData to a different value while having this useQuery instance mounted to get that effect. For example, if someDynamicData is part of a global client state or part of your URL, updating that would trigger a re-render of the subscribed components, thus triggering the query to “transition” towards the new key and to show placeholderData.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the response! my queryKey is intentionally static, and I suppose "previous" data is not what I meant -- I meant placeholder. My queryKey someDynamicData won't work for two reasons -- the components are far away it's hard to make them share state, and the state would be fake anyway; and the request to refresh the query goes out as soon as the query key changes. I need to put the query into a soft loading state (the existing data turns into placeholder data), wait for something else, and only then refresh the query.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.