@buoy-gg/react-query 5.0.0 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -357
- package/lib/commonjs/react-query/components/modals/QueryBrowserModal.js +5 -1
- package/lib/commonjs/react-query/components/query-browser/QueryBrowser.js +45 -10
- package/lib/commonjs/react-query/components/query-browser/QueryRow.js +8 -3
- package/lib/module/react-query/components/modals/QueryBrowserModal.js +5 -1
- package/lib/module/react-query/components/query-browser/QueryBrowser.js +46 -11
- package/lib/module/react-query/components/query-browser/QueryRow.js +8 -3
- package/lib/typescript/react-query/components/modals/QueryBrowserModal.d.ts.map +1 -1
- package/lib/typescript/react-query/components/query-browser/QueryBrowser.d.ts.map +1 -1
- package/lib/typescript/react-query/components/query-browser/QueryRow.d.ts +3 -1
- package/lib/typescript/react-query/components/query-browser/QueryRow.d.ts.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,389 +1,63 @@
|
|
|
1
|
-
# @buoy/react-query
|
|
1
|
+
# @buoy-gg/react-query
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@buoy-gg/react-query) [](https://www.npmjs.com/package/@buoy-gg/react-query)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Full TanStack Query devtools on the device — inspect the cache, simulate loading and error states, and flip your app offline with one tap.**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Part of [Buoy](https://github.com/Buoy-gg/buoy) — devtools that live inside your React Native app. Install it and it auto-appears in the floating menu from [`@buoy-gg/core`](https://www.npmjs.com/package/@buoy-gg/core).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- **Mutation Tracking**: Monitor mutations and their states
|
|
11
|
-
- **Cache Inspection**: View and manipulate the React Query cache
|
|
12
|
-
- **Query Invalidation**: Invalidate queries directly from the UI
|
|
13
|
-
- **Refetch Control**: Manually trigger query refetches
|
|
14
|
-
- **Filter & Search**: Filter queries by status, search by key
|
|
15
|
-
- **WiFi Toggle**: Simulate offline mode to test query behavior
|
|
16
|
-
- **Performance Monitoring**: Track query timings and performance
|
|
17
|
-
- **Beautiful UI**: Modern, game-themed interface matching other React Buoy tools
|
|
9
|
+

|
|
18
10
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
This package is part of the React Buoy monorepo and is automatically available to other packages and the example app.
|
|
22
|
-
|
|
23
|
-
For external projects:
|
|
11
|
+
## Install
|
|
24
12
|
|
|
25
13
|
```bash
|
|
26
|
-
npm install @buoy/
|
|
27
|
-
# or
|
|
28
|
-
pnpm add @buoy/react-query @tanstack/react-query
|
|
29
|
-
# or
|
|
30
|
-
yarn add @buoy/react-query @tanstack/react-query
|
|
14
|
+
npm install @buoy-gg/core @buoy-gg/react-query
|
|
31
15
|
```
|
|
32
16
|
|
|
33
|
-
|
|
17
|
+
Requires `@tanstack/react-query` as a peer — you already have it if you use React Query.
|
|
34
18
|
|
|
35
|
-
|
|
19
|
+
## Quick start
|
|
36
20
|
|
|
37
|
-
|
|
21
|
+
Render `FloatingDevTools` inside your `QueryClientProvider`. The tool picks up your `QueryClient` from context and connects automatically — no extra call needed.
|
|
38
22
|
|
|
39
|
-
```
|
|
40
|
-
import { reactQueryToolPreset } from '@buoy/react-query';
|
|
41
|
-
import { FloatingDevTools } from '@buoy/core';
|
|
23
|
+
```tsx
|
|
42
24
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
25
|
+
import { FloatingDevTools } from '@buoy-gg/core';
|
|
43
26
|
|
|
44
27
|
const queryClient = new QueryClient();
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
reactQueryToolPreset, // That's it! One line.
|
|
48
|
-
// ...your other tools
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
function App() {
|
|
29
|
+
export default function App() {
|
|
52
30
|
return (
|
|
53
31
|
<QueryClientProvider client={queryClient}>
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
environment="local"
|
|
57
|
-
userRole="admin"
|
|
58
|
-
/>
|
|
59
|
-
<YourAppContent />
|
|
32
|
+
<YourApp />
|
|
33
|
+
<FloatingDevTools />
|
|
60
34
|
</QueryClientProvider>
|
|
61
35
|
);
|
|
62
36
|
}
|
|
63
37
|
```
|
|
64
38
|
|
|
65
|
-
|
|
66
|
-
- ✅ Inspects all queries and mutations
|
|
67
|
-
- ✅ Provides cache manipulation tools
|
|
68
|
-
- ✅ Includes WiFi toggle for offline testing
|
|
69
|
-
- ✅ No configuration required
|
|
70
|
-
|
|
71
|
-
### Custom Configuration
|
|
72
|
-
|
|
73
|
-
If you want to customize the appearance:
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
import { createReactQueryTool } from '@buoy/react-query';
|
|
77
|
-
|
|
78
|
-
const myQueryTool = createReactQueryTool({
|
|
79
|
-
name: "TANSTACK",
|
|
80
|
-
colorPreset: "purple",
|
|
81
|
-
enableSharedModalDimensions: true,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const installedApps = [
|
|
85
|
-
myQueryTool,
|
|
86
|
-
// ...other tools
|
|
87
|
-
];
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Alternative: Manual Setup
|
|
91
|
-
|
|
92
|
-
If you're not using FloatingDevTools or want more control:
|
|
93
|
-
|
|
94
|
-
```typescript
|
|
95
|
-
import { ReactQueryDevToolsModal } from '@buoy/react-query';
|
|
96
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
97
|
-
|
|
98
|
-
const queryClient = new QueryClient();
|
|
99
|
-
|
|
100
|
-
function App() {
|
|
101
|
-
const [showQuery, setShowQuery] = useState(false);
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
<QueryClientProvider client={queryClient}>
|
|
105
|
-
<Button onPress={() => setShowQuery(true)}>
|
|
106
|
-
Open Query Inspector
|
|
107
|
-
</Button>
|
|
108
|
-
|
|
109
|
-
<ReactQueryDevToolsModal
|
|
110
|
-
visible={showQuery}
|
|
111
|
-
onClose={() => setShowQuery(false)}
|
|
112
|
-
/>
|
|
113
|
-
|
|
114
|
-
<YourAppContent />
|
|
115
|
-
</QueryClientProvider>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## API Reference
|
|
121
|
-
|
|
122
|
-
### Presets
|
|
123
|
-
|
|
124
|
-
#### `reactQueryToolPreset`
|
|
125
|
-
|
|
126
|
-
Pre-configured React Query devtools ready to use with FloatingDevTools.
|
|
127
|
-
|
|
128
|
-
**Example:**
|
|
129
|
-
```typescript
|
|
130
|
-
import { reactQueryToolPreset } from '@buoy/react-query';
|
|
131
|
-
|
|
132
|
-
const installedApps = [reactQueryToolPreset];
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
#### `createReactQueryTool(options?)`
|
|
136
|
-
|
|
137
|
-
Create a custom React Query devtools configuration.
|
|
138
|
-
|
|
139
|
-
**Options:**
|
|
140
|
-
```typescript
|
|
141
|
-
{
|
|
142
|
-
/** Tool name (default: "QUERY") */
|
|
143
|
-
name?: string;
|
|
144
|
-
/** Tool description */
|
|
145
|
-
description?: string;
|
|
146
|
-
/** Icon color preset (default: "red") */
|
|
147
|
-
colorPreset?: "orange" | "cyan" | "purple" | "pink" | "yellow" | "green" | "red";
|
|
148
|
-
/** Custom tool ID (default: "query") */
|
|
149
|
-
id?: string;
|
|
150
|
-
/** Enable shared modal dimensions (default: true) */
|
|
151
|
-
enableSharedModalDimensions?: boolean;
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
**Example:**
|
|
156
|
-
```typescript
|
|
157
|
-
import { createReactQueryTool } from '@buoy/react-query';
|
|
158
|
-
|
|
159
|
-
const myQueryTool = createReactQueryTool({
|
|
160
|
-
name: "RQ DEVTOOLS",
|
|
161
|
-
colorPreset: "cyan",
|
|
162
|
-
enableSharedModalDimensions: true,
|
|
163
|
-
});
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Components
|
|
167
|
-
|
|
168
|
-
#### `ReactQueryDevToolsModal`
|
|
169
|
-
|
|
170
|
-
Main modal component for React Query devtools.
|
|
171
|
-
|
|
172
|
-
**Props:**
|
|
173
|
-
```typescript
|
|
174
|
-
interface ReactQueryDevToolsModalProps {
|
|
175
|
-
/** Whether the modal is visible */
|
|
176
|
-
visible: boolean;
|
|
177
|
-
/** Callback when modal is closed */
|
|
178
|
-
onClose: () => void;
|
|
179
|
-
/** Whether to use shared modal dimensions (default: true) */
|
|
180
|
-
enableSharedModalDimensions?: boolean;
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
**Example:**
|
|
185
|
-
```typescript
|
|
186
|
-
<ReactQueryDevToolsModal
|
|
187
|
-
visible={isVisible}
|
|
188
|
-
onClose={handleClose}
|
|
189
|
-
enableSharedModalDimensions={true}
|
|
190
|
-
/>
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
#### `WifiToggle`
|
|
194
|
-
|
|
195
|
-
Small WiFi toggle component for simulating offline mode.
|
|
196
|
-
|
|
197
|
-
**Props:**
|
|
198
|
-
```typescript
|
|
199
|
-
interface WifiToggleProps {
|
|
200
|
-
/** Icon size (default: 16) */
|
|
201
|
-
size?: number;
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
**Example:**
|
|
206
|
-
```typescript
|
|
207
|
-
import { WifiToggle } from '@buoy/react-query';
|
|
208
|
-
|
|
209
|
-
<WifiToggle size={20} />
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Hooks
|
|
213
|
-
|
|
214
|
-
#### `useWifiState()`
|
|
215
|
-
|
|
216
|
-
Hook to manage and access WiFi/online state.
|
|
217
|
-
|
|
218
|
-
**Returns:**
|
|
219
|
-
```typescript
|
|
220
|
-
{
|
|
221
|
-
/** Whether the app is online */
|
|
222
|
-
isOnline: boolean;
|
|
223
|
-
/** Toggle WiFi state */
|
|
224
|
-
handleWifiToggle: () => void;
|
|
225
|
-
/** Set online state directly */
|
|
226
|
-
setIsOnline: (online: boolean) => void;
|
|
227
|
-
}
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
**Example:**
|
|
231
|
-
```typescript
|
|
232
|
-
import { useWifiState } from '@buoy/react-query';
|
|
233
|
-
|
|
234
|
-
function MyComponent() {
|
|
235
|
-
const { isOnline, handleWifiToggle } = useWifiState();
|
|
236
|
-
|
|
237
|
-
return (
|
|
238
|
-
<View>
|
|
239
|
-
<Text>Status: {isOnline ? 'Online' : 'Offline'}</Text>
|
|
240
|
-
<Button onPress={handleWifiToggle}>
|
|
241
|
-
Toggle WiFi
|
|
242
|
-
</Button>
|
|
243
|
-
</View>
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
## Use Cases
|
|
39
|
+
That's it. The React Query tool (and a WiFi toggle) appear in the floating menu.
|
|
249
40
|
|
|
250
|
-
|
|
41
|
+
## What you get
|
|
251
42
|
|
|
252
|
-
|
|
43
|
+
- **Cache inspection** — browse every query, drill into cached data with an interactive JSON tree, and edit values in place
|
|
44
|
+
- **Refetch & invalidate** — trigger refetches or invalidate queries straight from the UI
|
|
45
|
+
- **Simulate loading & error states** — test spinners and error boundaries without waiting on real network conditions
|
|
46
|
+
- **One-tap offline toggle** — the WiFi switch drives React Query's `onlineManager` to pause all queries, perfect for offline-first testing
|
|
47
|
+
- **Live mutation tracking** — status, variables, response or error, and timing for every mutation as it runs
|
|
48
|
+
- **Query states at a glance** — fresh, stale, fetching, paused, and inactive across your whole cache
|
|
49
|
+
- **Filter & search** — narrow by status or search by query key
|
|
253
50
|
|
|
254
|
-
|
|
255
|
-
import { reactQueryToolPreset } from '@buoy/react-query';
|
|
256
|
-
|
|
257
|
-
// Add to your dev tools
|
|
258
|
-
const installedApps = [reactQueryToolPreset];
|
|
259
|
-
|
|
260
|
-
// Now you can:
|
|
261
|
-
// - See all active queries
|
|
262
|
-
// - View query data and status
|
|
263
|
-
// - Inspect stale/fresh/loading states
|
|
264
|
-
// - Manually refetch queries
|
|
265
|
-
// - Invalidate queries to trigger refetch
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Offline Testing
|
|
269
|
-
|
|
270
|
-
Test your app's behavior in offline mode:
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
import { WifiToggle } from '@buoy/react-query';
|
|
274
|
-
|
|
275
|
-
// Add WiFi toggle to your header
|
|
276
|
-
<WifiToggle size={18} />
|
|
277
|
-
|
|
278
|
-
// Or use the hook directly
|
|
279
|
-
const { isOnline, handleWifiToggle } = useWifiState();
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
### Cache Manipulation
|
|
283
|
-
|
|
284
|
-
Inspect and manipulate the query cache:
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
// In the React Query devtools modal:
|
|
288
|
-
// - View all cached queries
|
|
289
|
-
// - See cache sizes and data
|
|
290
|
-
// - Manually invalidate cache entries
|
|
291
|
-
// - Trigger refetches
|
|
292
|
-
// - Remove queries from cache
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
### Mutation Monitoring
|
|
296
|
-
|
|
297
|
-
Track mutations and their states:
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
// In the React Query devtools modal:
|
|
301
|
-
// - View all active mutations
|
|
302
|
-
// - See mutation status (idle, loading, success, error)
|
|
303
|
-
// - Inspect mutation data and variables
|
|
304
|
-
// - Track mutation timing
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
## Features in Detail
|
|
308
|
-
|
|
309
|
-
### Query Browser
|
|
310
|
-
|
|
311
|
-
- **View All Queries**: See all queries registered with React Query
|
|
312
|
-
- **Filter by Status**: Filter by fresh, fetching, stale, or inactive
|
|
313
|
-
- **Search**: Search queries by key
|
|
314
|
-
- **Query Details**: View full query data, status, and metadata
|
|
315
|
-
- **Refetch**: Manually trigger query refetches
|
|
316
|
-
- **Invalidate**: Invalidate queries to trigger automatic refetch
|
|
317
|
-
- **Remove**: Remove queries from cache
|
|
318
|
-
|
|
319
|
-
### Mutation Tracking
|
|
320
|
-
|
|
321
|
-
- **View Mutations**: See all registered mutations
|
|
322
|
-
- **Mutation Status**: Track mutation states (idle, loading, success, error)
|
|
323
|
-
- **Variables**: Inspect mutation variables
|
|
324
|
-
- **Results**: View mutation results and errors
|
|
325
|
-
- **Reset**: Reset mutation states
|
|
326
|
-
|
|
327
|
-
### WiFi Toggle
|
|
328
|
-
|
|
329
|
-
- **Offline Mode**: Simulate offline state
|
|
330
|
-
- **Query Behavior**: Test how queries behave without network
|
|
331
|
-
- **Retry Logic**: Verify retry and error handling
|
|
332
|
-
- **Cache Fallbacks**: Test cache-first strategies
|
|
333
|
-
|
|
334
|
-
### Performance Monitoring
|
|
335
|
-
|
|
336
|
-
- **Query Timing**: See how long queries take
|
|
337
|
-
- **Cache Hits**: Track cache hit rates
|
|
338
|
-
- **Stale Time**: Monitor query freshness
|
|
339
|
-
- **Background Updates**: See when queries update in background
|
|
340
|
-
|
|
341
|
-
## React Query Integration
|
|
342
|
-
|
|
343
|
-
This package integrates with React Query's online manager to provide WiFi toggle functionality. When you toggle WiFi off:
|
|
344
|
-
|
|
345
|
-
- React Query treats the app as offline
|
|
346
|
-
- Queries won't refetch automatically
|
|
347
|
-
- New queries will be paused
|
|
348
|
-
- Mutations will be paused until online
|
|
349
|
-
- Background refetches are disabled
|
|
350
|
-
|
|
351
|
-
This is perfect for testing offline scenarios without actually disconnecting your device.
|
|
352
|
-
|
|
353
|
-
## Dependencies
|
|
354
|
-
|
|
355
|
-
- `@buoy/shared-ui` - Common UI components and utilities
|
|
356
|
-
- `@tanstack/react-query` - React Query library (peer dependency)
|
|
357
|
-
- React and React Native (peer dependencies)
|
|
358
|
-
|
|
359
|
-
## Development
|
|
360
|
-
|
|
361
|
-
### Building
|
|
362
|
-
|
|
363
|
-
```bash
|
|
364
|
-
pnpm build
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
### Type Checking
|
|
368
|
-
|
|
369
|
-
```bash
|
|
370
|
-
pnpm typecheck
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
### Clean Build
|
|
374
|
-
|
|
375
|
-
```bash
|
|
376
|
-
pnpm clean
|
|
377
|
-
```
|
|
51
|
+
## Desktop & AI
|
|
378
52
|
|
|
379
|
-
|
|
53
|
+
The same live session streams to [Buoy Desktop](https://github.com/Buoy-gg/Buoy-Desktop) (free, macOS/Windows/Linux) and to Claude Code or Cursor via the [Buoy MCP server](https://buoy.gg/buoy/latest/docs/mcp).
|
|
380
54
|
|
|
381
|
-
|
|
55
|
+
## Free vs Pro
|
|
382
56
|
|
|
383
|
-
|
|
57
|
+
Every tool is free. [Pro](https://buoy.gg/pricing) unlocks production builds, the MCP server, and unlimited capture. Every weekend, Pro features unlock free for everyone.
|
|
384
58
|
|
|
385
|
-
|
|
59
|
+
---
|
|
386
60
|
|
|
387
|
-
|
|
61
|
+
📚 [Full docs](https://buoy.gg/buoy/latest/docs/tools/react-query) · [All Buoy tools](https://github.com/Buoy-gg/buoy)
|
|
388
62
|
|
|
389
|
-
|
|
63
|
+
Proprietary software. © Buoy LLC. [Terms](https://buoy.gg/terms)
|
|
@@ -199,7 +199,11 @@ function QueryBrowserModal({
|
|
|
199
199
|
onModeChange: handleModeChange,
|
|
200
200
|
enablePersistence: true,
|
|
201
201
|
initialMode: "bottomSheet",
|
|
202
|
-
enableGlitchEffects: true
|
|
202
|
+
enableGlitchEffects: true
|
|
203
|
+
// The query list virtualizes itself (see QueryBrowser) — it must own
|
|
204
|
+
// scrolling. The filter view keeps the wrapper.
|
|
205
|
+
,
|
|
206
|
+
disableScrollWrapper: !showFilterView,
|
|
203
207
|
styles: {},
|
|
204
208
|
footer: !showFilterView ? footerNode : undefined,
|
|
205
209
|
footerHeight: !showFilterView ? 56 : undefined,
|
|
@@ -106,16 +106,51 @@ function QueryBrowser({
|
|
|
106
106
|
})
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
109
|
+
return (
|
|
110
|
+
/*#__PURE__*/
|
|
111
|
+
// Virtualized list with STABLE keys (queryHash). The previous
|
|
112
|
+
// ScrollView + map mounted every row, and its key included
|
|
113
|
+
// dataUpdatedAt/fetchStatus so rows REMOUNTED on each update — at 300
|
|
114
|
+
// seeded queries that measured 168% CPU with 546 row renders during a
|
|
115
|
+
// 5s idle dwell (tool-bench 2026-07-06). TanStack Query objects are
|
|
116
|
+
// mutable with stable identity, so `stateSig` carries the changing
|
|
117
|
+
// fields for the row's memo comparison.
|
|
118
|
+
(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
|
|
119
|
+
style: styles.listWrapper,
|
|
120
|
+
contentContainerStyle: contentContainerStyle || styles.listContent,
|
|
121
|
+
data: filteredQueries,
|
|
122
|
+
keyExtractor: queryKeyExtractor,
|
|
123
|
+
renderItem: ({
|
|
124
|
+
item: query
|
|
125
|
+
}) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_QueryRow.default, {
|
|
126
|
+
query: query,
|
|
127
|
+
stateSig: computeQueryStateSig(query),
|
|
128
|
+
isSelected: selectedQuery?.queryHash === query.queryHash,
|
|
129
|
+
onSelect: handleQuerySelect
|
|
130
|
+
}),
|
|
131
|
+
showsVerticalScrollIndicator: true,
|
|
132
|
+
removeClippedSubviews: true,
|
|
133
|
+
initialNumToRender: 12,
|
|
134
|
+
maxToRenderPerBatch: 8,
|
|
135
|
+
windowSize: 5,
|
|
136
|
+
updateCellsBatchingPeriod: 50
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
const queryKeyExtractor = q => q.queryHash;
|
|
141
|
+
|
|
142
|
+
/** The row-visible slice of a query's mutable state, as a comparable
|
|
143
|
+
* string — drives QueryRow's memo (see comment above). */
|
|
144
|
+
function computeQueryStateSig(q) {
|
|
145
|
+
let observers = 0;
|
|
146
|
+
let stale = false;
|
|
147
|
+
try {
|
|
148
|
+
observers = q.getObserversCount();
|
|
149
|
+
stale = q.isStale();
|
|
150
|
+
} catch {
|
|
151
|
+
// defensive: state reads on a disposed query must not break the list
|
|
152
|
+
}
|
|
153
|
+
return `${q.state.dataUpdatedAt}|${q.state.fetchStatus}|${q.state.status}|${observers}|${stale}`;
|
|
119
154
|
}
|
|
120
155
|
const styles = _reactNative.StyleSheet.create({
|
|
121
156
|
listWrapper: {
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
+
var _react = require("react");
|
|
7
8
|
var _getQueryStatusLabel = require("../../utils/getQueryStatusLabel");
|
|
8
9
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
10
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
@@ -15,11 +16,15 @@ const getQueryText = query => {
|
|
|
15
16
|
/**
|
|
16
17
|
* Single row representation of a query for the list view, showing status and observer count.
|
|
17
18
|
*/
|
|
18
|
-
|
|
19
|
+
// Memo justified by profiling (tool-bench 2026-07-06): 546 row renders in
|
|
20
|
+
// a 5s idle dwell at 300 queries. `stateSig` exists ONLY for this memo —
|
|
21
|
+
// Query objects are mutable with stable identity, so shallow-comparing
|
|
22
|
+
// `query` alone would never re-render (see QueryBrowser).
|
|
23
|
+
const QueryRow = /*#__PURE__*/(0, _react.memo)(function QueryRow({
|
|
19
24
|
query,
|
|
20
25
|
isSelected,
|
|
21
26
|
onSelect
|
|
22
|
-
})
|
|
27
|
+
}) {
|
|
23
28
|
// Buoy theme status color mapping
|
|
24
29
|
const getStatusHexColor = status => {
|
|
25
30
|
switch (status) {
|
|
@@ -76,5 +81,5 @@ const QueryRow = ({
|
|
|
76
81
|
isSelected: isSelected,
|
|
77
82
|
onPress: () => onSelect(query)
|
|
78
83
|
});
|
|
79
|
-
};
|
|
84
|
+
});
|
|
80
85
|
var _default = exports.default = QueryRow;
|
|
@@ -195,7 +195,11 @@ export function QueryBrowserModal({
|
|
|
195
195
|
onModeChange: handleModeChange,
|
|
196
196
|
enablePersistence: true,
|
|
197
197
|
initialMode: "bottomSheet",
|
|
198
|
-
enableGlitchEffects: true
|
|
198
|
+
enableGlitchEffects: true
|
|
199
|
+
// The query list virtualizes itself (see QueryBrowser) — it must own
|
|
200
|
+
// scrolling. The filter view keeps the wrapper.
|
|
201
|
+
,
|
|
202
|
+
disableScrollWrapper: !showFilterView,
|
|
199
203
|
styles: {},
|
|
200
204
|
footer: !showFilterView ? footerNode : undefined,
|
|
201
205
|
footerHeight: !showFilterView ? 56 : undefined,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { useMemo, useCallback } from "react";
|
|
4
|
-
import { View, StyleSheet, Text,
|
|
4
|
+
import { View, StyleSheet, Text, FlatList } from "react-native";
|
|
5
5
|
import QueryRow from "./QueryRow";
|
|
6
6
|
import useAllQueries from "../../hooks/useAllQueries";
|
|
7
7
|
import { getQueryStatusLabel } from "../../utils/getQueryStatusLabel";
|
|
@@ -101,16 +101,51 @@ export default function QueryBrowser({
|
|
|
101
101
|
})
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
104
|
+
return (
|
|
105
|
+
/*#__PURE__*/
|
|
106
|
+
// Virtualized list with STABLE keys (queryHash). The previous
|
|
107
|
+
// ScrollView + map mounted every row, and its key included
|
|
108
|
+
// dataUpdatedAt/fetchStatus so rows REMOUNTED on each update — at 300
|
|
109
|
+
// seeded queries that measured 168% CPU with 546 row renders during a
|
|
110
|
+
// 5s idle dwell (tool-bench 2026-07-06). TanStack Query objects are
|
|
111
|
+
// mutable with stable identity, so `stateSig` carries the changing
|
|
112
|
+
// fields for the row's memo comparison.
|
|
113
|
+
_jsx(FlatList, {
|
|
114
|
+
style: styles.listWrapper,
|
|
115
|
+
contentContainerStyle: contentContainerStyle || styles.listContent,
|
|
116
|
+
data: filteredQueries,
|
|
117
|
+
keyExtractor: queryKeyExtractor,
|
|
118
|
+
renderItem: ({
|
|
119
|
+
item: query
|
|
120
|
+
}) => /*#__PURE__*/_jsx(QueryRow, {
|
|
121
|
+
query: query,
|
|
122
|
+
stateSig: computeQueryStateSig(query),
|
|
123
|
+
isSelected: selectedQuery?.queryHash === query.queryHash,
|
|
124
|
+
onSelect: handleQuerySelect
|
|
125
|
+
}),
|
|
126
|
+
showsVerticalScrollIndicator: true,
|
|
127
|
+
removeClippedSubviews: true,
|
|
128
|
+
initialNumToRender: 12,
|
|
129
|
+
maxToRenderPerBatch: 8,
|
|
130
|
+
windowSize: 5,
|
|
131
|
+
updateCellsBatchingPeriod: 50
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
const queryKeyExtractor = q => q.queryHash;
|
|
136
|
+
|
|
137
|
+
/** The row-visible slice of a query's mutable state, as a comparable
|
|
138
|
+
* string — drives QueryRow's memo (see comment above). */
|
|
139
|
+
function computeQueryStateSig(q) {
|
|
140
|
+
let observers = 0;
|
|
141
|
+
let stale = false;
|
|
142
|
+
try {
|
|
143
|
+
observers = q.getObserversCount();
|
|
144
|
+
stale = q.isStale();
|
|
145
|
+
} catch {
|
|
146
|
+
// defensive: state reads on a disposed query must not break the list
|
|
147
|
+
}
|
|
148
|
+
return `${q.state.dataUpdatedAt}|${q.state.fetchStatus}|${q.state.status}|${observers}|${stale}`;
|
|
114
149
|
}
|
|
115
150
|
const styles = StyleSheet.create({
|
|
116
151
|
listWrapper: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
import { memo } from "react";
|
|
3
4
|
import { getQueryStatusLabel } from "../../utils/getQueryStatusLabel";
|
|
4
5
|
import { CompactRow, buoyColors, useRelativeTime } from "@buoy-gg/shared-ui";
|
|
5
6
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
@@ -11,11 +12,15 @@ const getQueryText = query => {
|
|
|
11
12
|
/**
|
|
12
13
|
* Single row representation of a query for the list view, showing status and observer count.
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
+
// Memo justified by profiling (tool-bench 2026-07-06): 546 row renders in
|
|
16
|
+
// a 5s idle dwell at 300 queries. `stateSig` exists ONLY for this memo —
|
|
17
|
+
// Query objects are mutable with stable identity, so shallow-comparing
|
|
18
|
+
// `query` alone would never re-render (see QueryBrowser).
|
|
19
|
+
const QueryRow = /*#__PURE__*/memo(function QueryRow({
|
|
15
20
|
query,
|
|
16
21
|
isSelected,
|
|
17
22
|
onSelect
|
|
18
|
-
})
|
|
23
|
+
}) {
|
|
19
24
|
// Buoy theme status color mapping
|
|
20
25
|
const getStatusHexColor = status => {
|
|
21
26
|
switch (status) {
|
|
@@ -72,5 +77,5 @@ const QueryRow = ({
|
|
|
72
77
|
isSelected: isSelected,
|
|
73
78
|
onPress: () => onSelect(query)
|
|
74
79
|
});
|
|
75
|
-
};
|
|
80
|
+
});
|
|
76
81
|
export default QueryRow;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryBrowserModal.d.ts","sourceRoot":"","sources":["../../../../../src/react-query/components/modals/QueryBrowserModal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAkB,MAAM,uBAAuB,CAAC;AAiBxE,UAAU,sBAAsB;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,QAAQ,CAAC;IAC5B,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,KAAK,IAAI,CAAC;IAClD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,WAAW,EAAE,CAAC,GAAG,EAAE,SAAS,GAAG,WAAW,KAAK,IAAI,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,gBAAgB,EAChB,aAAa,EACb,OAAO,EACP,UAAU,EACV,YAAY,EAAE,oBAAoB,EAClC,cAAc,EAAE,sBAAsB,EACtC,2BAAmC,EACnC,WAAW,EACX,UAAe,EACf,cAAc,GACf,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"QueryBrowserModal.d.ts","sourceRoot":"","sources":["../../../../../src/react-query/components/modals/QueryBrowserModal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAkB,MAAM,uBAAuB,CAAC;AAiBxE,UAAU,sBAAsB;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,QAAQ,CAAC;IAC5B,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,KAAK,IAAI,CAAC;IAClD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,WAAW,EAAE,CAAC,GAAG,EAAE,SAAS,GAAG,WAAW,KAAK,IAAI,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,gBAAgB,EAChB,aAAa,EACb,OAAO,EACP,UAAU,EACV,YAAY,EAAE,oBAAoB,EAClC,cAAc,EAAE,sBAAsB,EACtC,2BAAmC,EACnC,WAAW,EACX,UAAe,EACf,cAAc,GACf,EAAE,sBAAsB,sCA+NxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryBrowser.d.ts","sourceRoot":"","sources":["../../../../../src/react-query/components/query-browser/QueryBrowser.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"QueryBrowser.d.ts","sourceRoot":"","sources":["../../../../../src/react-query/components/query-browser/QueryBrowser.tsx"],"names":[],"mappings":"AACA,OAAO,EAAoC,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAM9C,UAAU,KAAK;IACb,aAAa,EAAE,KAAK,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,KAAK,IAAI,CAAC;IAClD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,SAAS,CAAC;IAClC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,aAAa,EACb,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACrB,OAAO,EAAE,eAAe,EACxB,UAAe,EACf,eAA2B,EAC3B,gBAA4B,GAC7B,EAAE,KAAK,+BA8IP"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Query } from "@tanstack/react-query";
|
|
2
2
|
interface QueryRowProps {
|
|
3
3
|
query: Query;
|
|
4
|
+
/** Changing state slice for the memo comparison — not read directly. */
|
|
5
|
+
stateSig: string;
|
|
4
6
|
isSelected: boolean;
|
|
5
7
|
onSelect: (query: Query) => void;
|
|
6
8
|
}
|
|
7
9
|
/**
|
|
8
10
|
* Single row representation of a query for the list view, showing status and observer count.
|
|
9
11
|
*/
|
|
10
|
-
declare const QueryRow:
|
|
12
|
+
declare const QueryRow: import("react").NamedExoticComponent<QueryRowProps>;
|
|
11
13
|
export default QueryRow;
|
|
12
14
|
//# sourceMappingURL=QueryRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryRow.d.ts","sourceRoot":"","sources":["../../../../../src/react-query/components/query-browser/QueryRow.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"QueryRow.d.ts","sourceRoot":"","sources":["../../../../../src/react-query/components/query-browser/QueryRow.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAiB9C,UAAU,aAAa;IACrB,KAAK,EAAE,KAAK,CAAC;IACb,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;GAEG;AAKH,QAAA,MAAM,QAAQ,qDA6DZ,CAAC;AAEH,eAAe,QAAQ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/react-query",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
|
+
"description": "TanStack Query devtools inside your React Native app — cache inspection, refetch & invalidate, offline toggle. Part of Buoy devtools.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
7
7
|
"types": "lib/typescript/index.d.ts",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
],
|
|
27
27
|
"sideEffects": false,
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@buoy-gg/shared-ui": "
|
|
30
|
-
"@buoy-gg/floating-tools-core": "
|
|
29
|
+
"@buoy-gg/shared-ui": "6.0.1",
|
|
30
|
+
"@buoy-gg/floating-tools-core": "6.0.1"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@tanstack/react-query": ">=5.0.0",
|