-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
🏝️ TanStack Query DevTools for Expo/React Native! 🚀 #8846
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
View your CI Pipeline Execution ↗ for commit daf0da1.
☁️ Nx Cloud last updated this comment at |
packages/query-core/src/query.ts
Outdated
@@ -133,6 +133,38 @@ interface ContinueAction { | |||
type: 'continue' | |||
} | |||
|
|||
export interface RefetchActionEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think these should be here. The actions listened on the NotifyEvents are events that are dispatched from our query reducer. Consumers can listen to those to observe updates that happen in the cache. When the devtools trigger an update that will result in an internal state change, those changes will automatically be propagated.
Can you explain why those were added?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any other ideas to capture action events that's reliable? I added these, so I know exactly what action is pressed to forward the action to mobile. it just seemed like the easiest approach to subscribe to query events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, I don’t understand what you’re trying to do. Why do you need to capture an event that happens in the devtools? Capture it where?
Assume I understand nothing about react native and expo (which is 99% true) and try to break down for me what you’re doing. If you want to listen to events from the devtools, doing that by dispatching an event on the queryCache and then listening to that is pretty likely not the right approach
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My plugin includes a web view that runs the React Query DevTools. The challenge I’m facing is linking DevTools actions to another client—in this case, mobile. I need to know when actions like refetch, invalidate, or set online/offline are triggered inside the DevTools UI.
Right now, the web view listens for those actions and sends a message to the mobile client to trigger the same action there. This works for all DevTools actions and online state changes.
If there's a simpler way to detect which action was pressed—along with the associated query and action type—I’d be happy to update the approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so should we make the devtools emit those events and you’d directly listen to those?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// 🌐 Web Client (DevTools in WebView) — Listening for Query Actions
// Subscribe to query changes
`const querySubscription = queryClient.getQueryCache().subscribe((event) => {
switch (event.type) {
case "updated":
switch (event.action.type as QueryActions) {
case "ACTION-REFETCH":
case "ACTION-INVALIDATE":
case "ACTION-TRIGGER-ERROR":
case "ACTION-RESTORE-ERROR":
case "ACTION-RESET":
case "ACTION-REMOVE":
case "ACTION-TRIGGER-LOADING":
case "ACTION-RESTORE-LOADING":
client.sendMessage("query-action", {
queryHash: event.query.queryHash,
queryKey: event.query.queryKey,
action: event.action.type as QueryActions,
targetDevice: selectedDeviceRef.current,
} as QueryActionMessage);
break;
case "success":
// @ts-ignore
if (event.action.manual) {
client.sendMessage("query-action", {
queryHash: event.query.queryHash,
queryKey: event.query.queryKey,
data: event.query.state.data,
action: "ACTION-DATA-UPDATE",
targetDevice: selectedDeviceRef.current,
} as QueryActionMessage);
}
break;
}
}
});
// 📱 Mobile Client — Handling Incoming Actions from Web DevTools
// Query Actions handler - Update query data, trigger errors, etc.
`const queryActionSubscription = client.addMessageListener(
"query-action",
(message: QueryActionMessage) => {
const { queryHash, queryKey, data, action, targetDevice } = message;
if (!shouldProcessMessage(targetDevice, Device.deviceName || "")) {
return;
}
const activeQuery = queryClient.getQueryCache().get(queryHash);
if (!activeQuery) {
console.warn(`Query with hash ${queryHash} not found`);
return;
}
switch (action) {
case "ACTION-DATA-UPDATE": {
queryClient.setQueryData(queryKey, data, {
updatedAt: Date.now(),
});
break;
}
case "ACTION-TRIGGER-ERROR": {
const error = new Error("Unknown error from devtools");
const __previousQueryOptions = activeQuery.options;
activeQuery.setState({
status: "error",
error,
fetchMeta: {
...activeQuery.state.fetchMeta,
// @ts-ignore
__previousQueryOptions,
},
});
break;
}
case "ACTION-RESTORE-ERROR": {
queryClient.resetQueries(activeQuery);
break;
}
case "ACTION-TRIGGER-LOADING": {
const __previousQueryOptions = activeQuery.options;
// Trigger a fetch that never resolves to simulate loading
activeQuery.fetch({
...__previousQueryOptions,
queryFn: () => new Promise(() => {}),
gcTime: -1,
});
activeQuery.setState({
data: undefined,
status: "pending",
fetchMeta: {
...activeQuery.state.fetchMeta,
// @ts-ignore
__previousQueryOptions,
},
});
break;
}
case "ACTION-RESTORE-LOADING": {
const previousState = activeQuery.state;
const previousOptions = activeQuery.state.fetchMeta
? (activeQuery.state.fetchMeta as any).__previousQueryOptions
: null;
activeQuery.cancel({ silent: true });
activeQuery.setState({
...previousState,
fetchStatus: "idle",
fetchMeta: null,
});
if (previousOptions) {
activeQuery.fetch(previousOptions);
}
break;
}
case "ACTION-RESET": {
queryClient.resetQueries(activeQuery);
break;
}
case "ACTION-REMOVE": {
queryClient.removeQueries(activeQuery);
break;
}
case "ACTION-REFETCH": {
activeQuery.fetch().catch(() => {});
break;
}
case "ACTION-INVALIDATE": {
queryClient.invalidateQueries(activeQuery);
break;
}
case "ACTION-ONLINE-MANAGER-ONLINE": {
onlineManager.setOnline(true);
break;
}
case "ACTION-ONLINE-MANAGER-OFFLINE": {
onlineManager.setOnline(false);
break;
}
}
}
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so should we make the devtools emit those events and you’d directly listen to those?
Yes!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, just to be on the same page: this would mean no changes to the query-core
, right? because those messages wouldn’t go through the query cache...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay great 👍
@TkDodo Okay, I updated this to use events instead and removed the other code. Thank you! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
knip says we don’t need to export those:
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #8846 +/- ##
===========================================
- Coverage 44.49% 15.72% -28.77%
===========================================
Files 206 34 -172
Lines 8150 2194 -5956
Branches 1829 557 -1272
===========================================
- Hits 3626 345 -3281
+ Misses 4081 1617 -2464
+ Partials 443 232 -211 🚀 New features to boost your workflow:
|
Added events for query actions to make it easy to capture them in external dev tools on mobile devices.
These events allow my external web interface to work seamlessly across any mobile framework or device using TanStack Query.
Plugin https://github.com/LovesWorking/tanstack-query-dev-tools-expo-plugin/tree/main
Example https://github.com/LovesWorking/RN-Dev-Tools-Example/tree/master
Mac App
https://github.com/LovesWorking/rn-better-dev-tools
Preview
iphone.testing.mov