diff --git a/.changeset/slimy-glasses-jog.md b/.changeset/slimy-glasses-jog.md new file mode 100644 index 000000000..d884f3616 --- /dev/null +++ b/.changeset/slimy-glasses-jog.md @@ -0,0 +1,6 @@ +--- +'@powersync/react': patch +'@powersync/react-native': patch +--- + +Fixed an issue with `useQuery` where initial query/parameter changes could cause a race condition if the first query took long. diff --git a/packages/react/src/hooks/useQuery.ts b/packages/react/src/hooks/useQuery.ts index 3559928cd..b5701027d 100644 --- a/packages/react/src/hooks/useQuery.ts +++ b/packages/react/src/hooks/useQuery.ts @@ -20,7 +20,7 @@ export type QueryResult = { /** * Function used to run the query again. */ - refresh?: () => Promise; + refresh?: (signal?: AbortSignal) => Promise; }; /** @@ -95,11 +95,16 @@ export const useQuery = ( setError(wrappedError); }; - const fetchData = async () => { + const fetchData = async (signal?: AbortSignal) => { setIsFetching(true); try { const result = typeof query == 'string' ? await powerSync.getAll(sqlStatement, queryParameters) : await query.execute(); + + if (signal?.aborted) { + return; + } + handleResult(result); } catch (e) { console.error('Failed to fetch data:', e); @@ -107,9 +112,14 @@ export const useQuery = ( } }; - const fetchTables = async () => { + const fetchTables = async (signal?: AbortSignal) => { try { const tables = await powerSync.resolveTables(sqlStatement, memoizedParams, memoizedOptions); + + if (signal?.aborted) { + return; + } + setTables(tables); } catch (e) { console.error('Failed to fetch tables:', e); @@ -118,9 +128,10 @@ export const useQuery = ( }; React.useEffect(() => { + const abortController = new AbortController(); const updateData = async () => { - await fetchTables(); - await fetchData(); + await fetchTables(abortController.signal); + await fetchData(abortController.signal); }; updateData(); @@ -129,7 +140,10 @@ export const useQuery = ( schemaChanged: updateData }); - return () => l?.(); + return () => { + abortController.abort(); + l?.(); + }; }, [powerSync, memoizedParams, sqlStatement]); React.useEffect(() => { @@ -141,7 +155,7 @@ export const useQuery = ( powerSync.onChangeWithCallback( { onChange: async () => { - await fetchData(); + await fetchData(abortController.current.signal); }, onError(e) { handleError(e);