-
I have a situation where I need all the data from function randomDelay() {
return new Promise(resolve => {
setTimeout(resolve, Math.random() * 100);
})
}
let counter = 0;
async function query1() {
await randomDelay();
if(counter % 2 == 0) {
return [{id: 1, text: "one", color: "red"}, { id: 2, text: "two", color: "red"}];
} else {
return [{id: 1, text: "one", color: "red"}];
}
}
async function query2() {
await randomDelay();
if(counter % 2 == 0) {
return [{id: 3, text: "three", color: "green"}];
} else {
return [{id: 2, text: "two", color: "green"}, {id: 3, text: "three", color: "green"}];
}
}
function Test() {
const [q1, q2] = useSuspenseQueries({queries: [
{
queryFn: query1,
queryKey: ["test", "q1"]
},
{
queryFn: query2,
queryKey: ["test", "q2"]
}
]});
const allData = q1.data.concat(q2.data);
const queryClient = useQueryClient();
return (
<div>
<button onClick={() => (++counter, queryClient.invalidateQueries({queryKey: ["test"]}))}>Do the thing</button>
<ol>
{allData.map(({id, text, color}) => <li key={id} style={{color}}>{text}</li>)}
</ol>
</div>
)
} Clicking the Note I am aware that to some extent, what I'm asking for is unsound, because querying two API endpoints is not really atomic, so even on the first request, the two endpoints may be out of sync with each other. In practice, I'm willing to ignore the situation where the client requests endpoint A, the data changes, and then the client requests endpoint B. 1st question: Is there anything out of the box to support this behavior? 2nd question: If not, I have created a function queryHashesMatch(curHash: string[], prevHash: string[]) {
if(curHash.length != prevHash.length) {
return false;
}
return curHash.every((_, i) => curHash[i] === prevHash[i]);
}
function hashKey(queryKey: QueryKey): string {
return JSON.stringify(queryKey);
}
type ResolvablePromise<T> = Promise<T> & {resolve: (t: T) => void}
function resolvablePromise<T>() : ResolvablePromise<T> {
let resolve: (t: T) => void;
let ret = new Promise<T>((_resolve) => { resolve = _resolve; });
(ret as ResolvablePromise<T>).resolve = resolve!;
return (ret as ResolvablePromise<T>);
}
function useAtomicSuspenseQueries<T extends Array<any>>(options: {
queries: readonly [...SuspenseQueriesOptions<T>];
}, queryClient?: QueryClient): SuspenseQueriesResults<T> {
const resultsRef = useRef<SuspenseQueriesResults<T> | null>(null);
const hashRef = useRef<string[] | null>(null);
const resolvablePromiseRef = useRef<ResolvablePromise<SuspenseQueriesResults<T>> | null>(null);
const curHash = options.queries.map(a => hashKey(a.queryKey));
if(!hashRef.current) {
hashRef.current = curHash;
} else if(!queryHashesMatch(curHash, hashRef.current)) {
hashRef.current = curHash;
resultsRef.current = null;
resolvablePromiseRef.current = null;
}
const results = useSuspenseQueries({queries: options.queries}, queryClient);
const isStale = results.reduce((c, {isStale}) => c || isStale, false);
if(isStale) {
if(resultsRef.current) {
return resultsRef.current;
} else {
resolvablePromiseRef.current = resolvablePromise();
React.use(resolvablePromiseRef.current); // or throw resolvablePromiseRef.current
}
} else {
resultsRef.current = results;
resolvablePromiseRef.current?.resolve(results);
}
return resultsRef.current!;
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
you can either use |
Beta Was this translation helpful? Give feedback.
you can either use
combine
and return a “pending” state if one of the queries is still fetching, or combine the multiple requests into one query that fires multiple requests withPromise.all
.