How do I handle a single-request asynchronous query? #5566
-
I have an atypical query that rather than doing a fetch request it calls a function, sets an event and waits for the event to get called before retrieving and returning the data. Because of this I need to make sure that if this query is requested again while it's waiting, it should request cancellation of the previous query and then run a new one, the useQuery instance is on a context, which (I hope) means there's only ever a single source for it, there's no worrying that multiple controls may call this function at the same time, however since it's on a searchbar it may be requested again before the previous one is done. I haven't been able to figure this out, what would be the best way to have this behavior? This is my current query, I already handled canceling but right now the cancellation is never done because calling the query again results in just another query, parallel to this. const collections = useQuery(queryDatabase.collections()); // this is a regular Fetch query
const { data, isLoading, isError, isSuccess, isIdle, error } = useQuery(
[ `search`, inputText, searchFilters ], // searchFilters is an object rather than a string
newSearchCall,
{
enabled: !collections.isLoading && collections.isSuccess && collections.data && inputText !== '', // Waits for collection query to have data and for the input text to be a valid string
meta: {
cache: false, // This prevents saving the cache. Needed to avoid restoring stale data on new app starts, it's used with a PersistQueryClientProvider and checked in its shouldDehydrateQuery function
},
cacheTime: 5 * 60 * 1000, // 5 minutes, it's already set to not persist
}
);
function newSearchCall({signal}) {
let cancelSearch = false;
if (signal.aborted) {
cancelSearch = true;
Plugin.postMessage({ Type: 'CancelSearch' });
console.log('Search aborted');
}
return new Promise((resolve, reject) => {
// Call Plugin.postMessage() and wait for results to be ready
const message = wrapSearch(inputText, searchFilters);
Plugin.postMessage(message);
// Function to handle the results when ready
async function handleResults() {
if (cancelSearch) {
window.removeEventListener('searchResultsReady', handleResults);
return reject('Search cancelled');
}
let results = normalizeKeys(JSON.parse(await Plugin.hostObjects.searchResults.LastResultsJson));
let resultsCount = await Plugin.hostObjects.searchResults.TotalResults;
resolve({ results, resultsCount });
// Remove the event listener after resolve
window.removeEventListener('searchResultsReady', handleResults);
}
// Listen for the showDetailsAsync function to be called
window.addEventListener('searchResultsReady', handleResults);
});
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 8 replies
-
This might help. |
Beta Was this translation helpful? Give feedback.
Thank you, it wasn't as straightforward from the example because fetch and axios handle cancellation automatically but in my case I had to do it manually, just checking for signal.aborted sufficed.
Also I realized far too late I had a bunch of mistakes in my code that made things worse, like missing a negation on the enable condition and not returning the data properly from the promise.
Thanks to the example I managed to do it without any explicit cancellations, now the cancellation is properly called and the final search function looks like this: