1
1
import { type CompilableQuery , parseQuery } from '@powersync/common' ;
2
2
import { usePowerSync } from '@powersync/react' ;
3
3
import * as Tanstack from '@tanstack/react-query' ;
4
- import { useEffect , useMemo , useState } from 'react' ;
4
+ import { useEffect , useMemo , useState , useCallback } from 'react' ;
5
5
6
6
export type PowerSyncQueryOptions < T > = {
7
7
query ?: string | CompilableQuery < T > ;
8
8
parameters ?: any [ ] ;
9
9
} ;
10
10
11
- export type UseBaseQueryOptions < TQueryOptions > = TQueryOptions & PowerSyncQueryOptions < any > ;
12
-
13
- export type PowerSyncQueriesOptions < T extends any [ ] > = {
14
- [ K in keyof T ] : Tanstack . UseQueryOptions < T [ K ] , any > & PowerSyncQueryOptions < T [ K ] > ;
15
- } ;
16
-
17
11
type PowerSyncQueryOption < T = any > = Tanstack . UseQueryOptions < T [ ] , any > & PowerSyncQueryOptions < T > ;
18
12
19
13
type InferQueryResults < TQueries extends readonly any [ ] > = {
@@ -75,8 +69,10 @@ export function useQueries<TQueries extends readonly PowerSyncQueryOption[]>(
75
69
// Implementation
76
70
export function useQueries (
77
71
options : {
78
- queries : readonly ( Tanstack . UseQueryOptions < any , any > & PowerSyncQueryOptions < any > ) [ ] ;
79
- combine ?: ( results : ( Tanstack . UseQueryResult < any , any > & { queryKey : Tanstack . QueryKey } ) [ ] ) => any ;
72
+ queries : readonly ( Tanstack . UseQueryOptions & PowerSyncQueryOptions < unknown > ) [ ] ;
73
+ combine ?: (
74
+ results : ( Tanstack . UseQueryResult < unknown , Tanstack . DefaultError > & { queryKey : Tanstack . QueryKey } ) [ ]
75
+ ) => unknown ;
80
76
} ,
81
77
queryClient : Tanstack . QueryClient = Tanstack . useQueryClient ( )
82
78
) {
@@ -85,24 +81,56 @@ export function useQueries(
85
81
const [ tablesArr , setTablesArr ] = useState < string [ ] [ ] > ( ( ) => queriesInput . map ( ( ) => [ ] ) ) ;
86
82
const [ errorsArr , setErrorsArr ] = useState < ( Error | undefined ) [ ] > ( ( ) => queriesInput . map ( ( ) => undefined ) ) ;
87
83
88
- const parsedQueries = useMemo ( ( ) => {
89
- return queriesInput . map ( ( queryOptions : Tanstack . UseQueryOptions < any , any > & PowerSyncQueryOptions < any > ) => {
90
- const { query, parameters = [ ] , ...rest } = queryOptions ;
91
- let sqlStatement = '' ;
92
- let queryParameters : any [ ] = [ ] ;
93
- let error : Error | undefined = undefined ;
94
- if ( query ) {
95
- try {
96
- const parsedQuery = parseQuery ( query , parameters ) ;
97
- sqlStatement = parsedQuery . sqlStatement ;
98
- queryParameters = parsedQuery . parameters ;
99
- } catch ( e ) {
100
- error = e as Error ;
101
- }
102
- }
103
- return { query, parameters, rest, sqlStatement, queryParameters, error } ;
84
+ const updateTablesArr = useCallback ( ( tables : string [ ] , idx : number ) => {
85
+ setTablesArr ( ( prev ) => {
86
+ if ( JSON . stringify ( prev [ idx ] ) === JSON . stringify ( tables ) ) return prev ;
87
+ const next = [ ...prev ] ;
88
+ next [ idx ] = tables ;
89
+ return next ;
90
+ } ) ;
91
+ } , [ ] ) ;
92
+
93
+ const updateErrorsArr = useCallback ( ( error : Error , idx : number ) => {
94
+ setErrorsArr ( ( prev ) => {
95
+ if ( prev [ idx ] ?. message === error . message ) return prev ;
96
+ const next = [ ...prev ] ;
97
+ next [ idx ] = error ;
98
+ return next ;
104
99
} ) ;
105
- } , [ queriesInput ] ) ;
100
+ } , [ ] ) ;
101
+
102
+ const parsedQueries = useMemo ( ( ) => queriesInput . map ( ( queryOptions : Tanstack . UseQueryOptions < any , any > & PowerSyncQueryOptions < any > ) => {
103
+ const { query, parameters = [ ] , ...rest } = queryOptions ;
104
+ const parsed = ( ( ) => {
105
+ if ( ! query ) {
106
+ return { sqlStatement : '' , queryParameters : [ ] , error : undefined } ;
107
+ }
108
+
109
+ try {
110
+ const parsedQuery = parseQuery ( query , parameters ) ;
111
+ return {
112
+ sqlStatement : parsedQuery . sqlStatement ,
113
+ queryParameters : parsedQuery . parameters ,
114
+ error : undefined
115
+ } ;
116
+ } catch ( e ) {
117
+ return {
118
+ sqlStatement : '' ,
119
+ queryParameters : [ ] ,
120
+ error : e as Error
121
+ } ;
122
+ }
123
+ } ) ( ) ;
124
+
125
+ return { query, parameters, rest, ...parsed } ;
126
+ } ) , [ queriesInput ] ) ;
127
+
128
+ const stringifiedQueriesDeps = JSON . stringify (
129
+ parsedQueries . map ( q => ( {
130
+ sql : q . sqlStatement ,
131
+ params : q . queryParameters ,
132
+ } ) )
133
+ ) ;
106
134
107
135
useEffect ( ( ) => {
108
136
const listeners : ( undefined | ( ( ) => void ) ) [ ] = [ ] ;
@@ -111,39 +139,19 @@ export function useQueries(
111
139
( async ( ) => {
112
140
try {
113
141
const t = await powerSync . resolveTables ( q . sqlStatement , q . queryParameters ) ;
114
- setTablesArr ( ( prev ) => {
115
- if ( JSON . stringify ( prev [ idx ] ) === JSON . stringify ( t ) ) return prev ;
116
- const next = prev . slice ( ) ;
117
- next [ idx ] = t ;
118
- return next ;
119
- } ) ;
142
+ updateTablesArr ( t , idx ) ;
120
143
} catch ( e ) {
121
- setErrorsArr ( ( prev ) => {
122
- if ( prev [ idx ] ?. message === ( e as Error ) . message ) return prev ;
123
- const next = prev . slice ( ) ;
124
- next [ idx ] = e as Error ;
125
- return next ;
126
- } ) ;
144
+ updateErrorsArr ( e , idx ) ;
127
145
}
128
146
} ) ( ) ;
129
147
const l = powerSync . registerListener ( {
130
148
schemaChanged : async ( ) => {
131
149
try {
132
150
const t = await powerSync . resolveTables ( q . sqlStatement , q . queryParameters ) ;
133
- setTablesArr ( ( prev ) => {
134
- if ( JSON . stringify ( prev [ idx ] ) === JSON . stringify ( t ) ) return prev ;
135
- const next = prev . slice ( ) ;
136
- next [ idx ] = t ;
137
- return next ;
138
- } ) ;
151
+ updateTablesArr ( t , idx ) ;
139
152
queryClient . invalidateQueries ( { queryKey : q . rest . queryKey } ) ;
140
153
} catch ( e ) {
141
- setErrorsArr ( ( prev ) => {
142
- if ( prev [ idx ] ?. message === ( e as Error ) . message ) return prev ;
143
- const next = prev . slice ( ) ;
144
- next [ idx ] = e as Error ;
145
- return next ;
146
- } ) ;
154
+ updateErrorsArr ( e , idx ) ;
147
155
}
148
156
} ,
149
157
} ) ;
@@ -153,7 +161,9 @@ export function useQueries(
153
161
return ( ) => {
154
162
listeners . forEach ( ( l ) => l ?.( ) ) ;
155
163
} ;
156
- } , [ powerSync , parsedQueries , queryClient ] ) ;
164
+ } , [ powerSync , queryClient , stringifiedQueriesDeps , updateErrorsArr , updateTablesArr ] ) ;
165
+
166
+ const stringifiedQueryKeys = JSON . stringify ( parsedQueries . map ( ( q ) => q . rest . queryKey ) ) ;
157
167
158
168
useEffect ( ( ) => {
159
169
const aborts : AbortController [ ] = [ ] ;
@@ -167,12 +177,7 @@ export function useQueries(
167
177
queryClient . invalidateQueries ( { queryKey : q . rest . queryKey } ) ;
168
178
} ,
169
179
onError : ( e ) => {
170
- setErrorsArr ( ( prev ) => {
171
- if ( prev [ idx ] ?. message === ( e as Error ) . message ) return prev ;
172
- const next = prev . slice ( ) ;
173
- next [ idx ] = e as Error ;
174
- return next ;
175
- } ) ;
180
+ updateErrorsArr ( e , idx ) ;
176
181
} ,
177
182
} ,
178
183
{
@@ -182,13 +187,14 @@ export function useQueries(
182
187
) ;
183
188
} ) ;
184
189
return ( ) => aborts . forEach ( ( a ) => a ?. abort ( ) ) ;
185
- } , [ powerSync , parsedQueries , queryClient , tablesArr ] ) ;
190
+ } , [ powerSync , queryClient , tablesArr , updateErrorsArr , stringifiedQueryKeys ] ) ;
186
191
187
192
const queries = useMemo ( ( ) => {
188
193
return parsedQueries . map ( ( q , idx ) => {
189
194
const error = q . error || errorsArr [ idx ] ;
190
195
const queryFn = async ( ) => {
191
196
if ( error ) throw error ;
197
+
192
198
try {
193
199
return typeof q . query === 'string'
194
200
? powerSync . getAll ( q . sqlStatement , q . queryParameters )
@@ -203,19 +209,19 @@ export function useQueries(
203
209
queryKey : q . rest . queryKey ,
204
210
} ;
205
211
} ) ;
206
- } , [ parsedQueries , errorsArr , powerSync ] ) ;
212
+ } , [ stringifiedQueriesDeps , errorsArr ] ) ;
207
213
208
214
return Tanstack . useQueries (
209
215
{
210
216
queries : queries as Tanstack . QueriesOptions < any > ,
211
217
combine : options . combine
212
218
? ( results ) => {
213
- const enhancedResults = results . map ( ( result , index ) => ( {
219
+ const enhancedResultsWithQueryKey = results . map ( ( result , index ) => ( {
214
220
...result ,
215
221
queryKey : queries [ index ] . queryKey ,
216
222
} ) ) ;
217
223
218
- return options . combine ?.( enhancedResults ) ;
224
+ return options . combine ?.( enhancedResultsWithQueryKey as any ) ;
219
225
}
220
226
: undefined ,
221
227
} ,
0 commit comments