Skip to content

Commit 3b5b757

Browse files
committed
Actionsheet devtool debugger
1 parent 1cce20e commit 3b5b757

5 files changed

Lines changed: 330 additions & 41 deletions

File tree

packages/react-native-query-devtool/README.md

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,21 @@
33
## Features
44

55
- Provides a component for integrating query data in React Native applications running react-query v3, and @tanstack/react-query v4 and v5.
6-
- Includes a standalone app to visualize and help debug query data.
7-
8-
## Standalone App
9-
10-
The standalone app allows you to visualize and debug query data from your React Native application using the `@jsmdeleon/react-native-query-devtool` package.
6+
- (Optional) Includes a standalone app to visualize and help debug query data.
117

128
## Installation
139

14-
You can download the standalone app from the following link. Currently, it's available only for **macOS M1** and **Windows x64**:
15-
16-
[Download latest App](https://github.com/jossydeleon/react-native-query-devtool-monorepo/releases)
17-
18-
**Linux users:** You need to build the app from source as it's currently only available for **macOS M1** and **Windows x64**. Ensure to set up your environment accordingly before building the app.
10+
To use the devtool component in your React Native project, install the package using `npm` or `yarn`, You will also need to install `react-native-gesture-handlers` if you haven't already.
1911

2012
```bash
21-
# Go to app folder
22-
cd react-native-query-devtool-app
23-
24-
# Install dependencies
25-
yarn
26-
27-
# Build
28-
yarn build-server
29-
```
30-
31-
After running the build command, you'll find the freshly built app in a new folder named **'out'**.
32-
33-
## React Native Component
34-
35-
To use the devtool component in your React Native project, install the package:
36-
37-
```bash
38-
npm install @jsmdeleon/react-native-query-devtool
13+
npm install react-native-gesture-handler @jsmdeleon/react-native-query-devtool
3914
# or
40-
yarn add @jsmdeleon/react-native-query-devtool
15+
yarn add react-native-gesture-handler @jsmdeleon/react-native-query-devtool
4116
```
4217

4318
## Usage
4419

45-
In your React Native application, import `QueryNativeDevtool` to send query data to the Electron app for debugging:
20+
In your React Native application, import `QueryNativeDevtool`
4621

4722
```javascript
4823
import { QueryNativeDevtool } from "@jsmdeleon/react-native-query-devtool";
@@ -53,7 +28,7 @@ export default function App() {
5328
<View style={styles.container}>
5429
<Main />
5530
</View>
56-
<QueryNativeDevtool queryClient={queryClient} />
31+
<QueryNativeDevtool queryClient={queryClient} useRemoteDebugger={false} />
5732
</QueryClientProvider>
5833
);
5934
}
@@ -63,22 +38,49 @@ export default function App() {
6338

6439
If your app runs `react-query v3` pass `version="v3"` prop to `QueryNativeDevtool`
6540

66-
```javascript
67-
import { QueryNativeDevtool } from "@jsmdeleon/react-native-query-devtool";
41+
## Available Props
42+
43+
| Prop Name | Type | Description | Default Value |
44+
| -------------------- | ----------- | ------------------------------------------ | ------------- |
45+
| `queryClient`\* | QueryClient | First name of the user | undefined |
46+
| `version` | string | Last name of the user | "v5" |
47+
| `hideFloatingButton` | boolean | Hides Floating button | false |
48+
| `useRemoteDebugger` | boolean | Enable remote debugging via standalone app | false |
49+
50+
## Standalone app (Optional)
6851

52+
If you want more room to debug your query data, you can download the standalone app from the following link. Currently, it's available only for **macOS M1** and **Windows x64**. See [repo](https://github.com/jossydeleon/react-native-query-devtool-monorepo/tree/main/packages/react-native-query-devtool-app)
53+
54+
```javascript
6955
export default function App() {
7056
return (
7157
<QueryClientProvider client={queryClient}>
72-
<View style={styles.container}>
73-
<Main />
74-
</View>
75-
<QueryNativeDevtool queryClient={queryClient} version="v3" />
58+
<QueryNativeDevtool queryClient={queryClient} useRemoteDebugger={true} />
7659
</QueryClientProvider>
7760
);
7861
}
7962
```
8063

81-
This will enable your React Native application to send query data to the server app for debugging purposes.
64+
[Download latest App](https://github.com/jossydeleon/react-native-query-devtool-monorepo/releases)
65+
66+
**Note:** The binaries provided are not signed. If you have concerns about security or trust, you can choose to build the app from the source code for your machine.
67+
68+
**Linux users:** You need to build the app from source as it's currently only available for **macOS M1** and **Windows x64**. Ensure to set up your environment accordingly before building the app.
69+
70+
```bash
71+
# Go to app folder
72+
cd react-native-query-devtool-app
73+
74+
# Install dependencies
75+
yarn
76+
77+
# Build
78+
yarn build-server
79+
```
80+
81+
After running the build command, you'll find the freshly built app in a new folder named **'out'**.
82+
83+
**Important:** The standalone app uses port `9017` by default. Ensure that this port is available and not blocked by any firewall settings or other applications on your system.
8284

8385
## Examples
8486

@@ -98,3 +100,7 @@ yarn ios
98100
# or
99101
yarn android
100102
```
103+
104+
## Contributions
105+
106+
Contributions to improve this project are welcome! If you have any ideas for new features, enhancements, bug fixes, or optimizations, feel free to submit a pull request. You can also report any bugs or issues you encounter by opening a new issue
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import React from "react";
2+
3+
import { ScrollView, Text, View } from "react-native";
4+
5+
import {
6+
ScrollView as ActionsheetScrollview,
7+
FlatList,
8+
} from "react-native-actions-sheet";
9+
10+
import { QueryDevtoolProps } from "../../types";
11+
import CloseButton from "../CloseButton";
12+
import DataExplorer from "../DataExplorer";
13+
import QueryRow from "../QueryRow";
14+
import Searchbar from "../Searchbar";
15+
import useDevtoolData from "./useDevtoolData";
16+
17+
import { styles } from "./styles";
18+
19+
const Devtool: React.FC<QueryDevtoolProps> = (props) => {
20+
const {
21+
filteredQueries,
22+
selectedQuery,
23+
filter,
24+
setFilter,
25+
handleSelectedRow,
26+
} = useDevtoolData(props);
27+
28+
const renderHeader = () => (
29+
<View style={styles.searchbarContainer}>
30+
<Searchbar filter={filter} setFilter={setFilter} />
31+
</View>
32+
);
33+
34+
const renderDataExplorerHeader = () => {
35+
if (!selectedQuery) return null;
36+
37+
return (
38+
<>
39+
<View style={styles.headerContainer}>
40+
<Text style={styles.headerText}>Data Explorer</Text>
41+
<CloseButton onPress={() => handleSelectedRow()} />
42+
</View>
43+
</>
44+
);
45+
};
46+
47+
const renderDataExplorer = () => {
48+
if (!selectedQuery) return null;
49+
50+
return (
51+
<View style={styles.dataExplorerContainer}>
52+
<DataExplorer selectedQuery={selectedQuery?.data} />
53+
</View>
54+
);
55+
};
56+
57+
return (
58+
<ActionsheetScrollview
59+
nestedScrollEnabled
60+
stickyHeaderIndices={[0, 2]}
61+
style={styles.actionScrollviewContainer}
62+
>
63+
{renderHeader()}
64+
<ScrollView horizontal scrollEnabled={false}>
65+
<FlatList
66+
data={filteredQueries}
67+
style={[
68+
styles.queriesFlatlist,
69+
selectedQuery ? styles.collapsedFlatlist : styles.expandedFlatlist,
70+
]}
71+
renderItem={({ item, index }) => (
72+
<QueryRow
73+
index={index}
74+
item={item}
75+
isSelected={item.queryKey === selectedQuery?.queryKey}
76+
handleSelectedRow={handleSelectedRow}
77+
/>
78+
)}
79+
ItemSeparatorComponent={() => <View style={styles.separator} />}
80+
ListEmptyComponent={
81+
<Text style={styles.flatlistEmptyMessage}>No queries found</Text>
82+
}
83+
/>
84+
</ScrollView>
85+
86+
{renderDataExplorerHeader()}
87+
{renderDataExplorer()}
88+
</ActionsheetScrollview>
89+
);
90+
};
91+
92+
export default Devtool;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Dimensions, StyleSheet } from "react-native";
2+
3+
export const styles = StyleSheet.create({
4+
actionScrollviewContainer: {
5+
minHeight: 250,
6+
maxHeight: 500,
7+
},
8+
searchbarContainer: {
9+
backgroundColor: "#0b1521",
10+
padding: 2,
11+
paddingBottom: 1,
12+
},
13+
headerContainer: {
14+
flexDirection: "row",
15+
justifyContent: "space-between",
16+
alignItems: "center",
17+
backgroundColor: "#132337",
18+
padding: 10,
19+
borderTopLeftRadius: 10,
20+
borderTopRightRadius: 10,
21+
},
22+
headerText: {
23+
color: "white",
24+
},
25+
queriesContainer: {
26+
padding: 10,
27+
},
28+
queriesFlatlist: {
29+
width: Dimensions.get("screen").width,
30+
paddingHorizontal: 2,
31+
},
32+
flatlistEmptyMessage: {
33+
color: "white",
34+
textAlign: "center",
35+
},
36+
expandedFlatlist: {
37+
height: 500,
38+
},
39+
collapsedFlatlist: {
40+
height: 250,
41+
},
42+
separator: {
43+
borderBottomWidth: 0.3,
44+
borderColor: "silver",
45+
marginVertical: 5,
46+
},
47+
dataExplorerContainer: {
48+
marginBottom: 20,
49+
},
50+
});
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2+
3+
import {
4+
ListenerEventType,
5+
QueryDevtoolData,
6+
QueryDevtoolProps,
7+
QueryKey,
8+
} from '../../types';
9+
import {
10+
getQueryDevtoolData,
11+
handleQueryDevtoolData,
12+
} from '../../utils/queryListener';
13+
14+
const useDevtoolData = (props: QueryDevtoolProps) => {
15+
const { queryClient, version = 'v5' } = props;
16+
17+
const isInitialized = useRef(false);
18+
19+
const [queries, setQueries] = useState<QueryDevtoolData[]>([]);
20+
const [selectedQueryKey, setSelectedQueryKey] = useState<QueryKey>();
21+
const [filter, setFilter] = useState('');
22+
23+
useEffect(() => {
24+
if (isInitialized.current) return;
25+
26+
const allQueries = queryClient
27+
.getQueryCache()
28+
.getAll()
29+
.map((queriesData: any) => getQueryDevtoolData(version, queriesData));
30+
31+
setQueries(allQueries);
32+
33+
isInitialized.current = true;
34+
}, [version, queryClient]);
35+
36+
useEffect(() => {
37+
const subscribe = queryClient.getQueryCache().subscribe((listener: any) => {
38+
if (!listener) {
39+
return;
40+
}
41+
42+
const listenerType: ListenerEventType = listener.type;
43+
const queryKey = listener.query.queryKey;
44+
45+
if (!queryKey) {
46+
return;
47+
}
48+
49+
const query = listener.query;
50+
51+
setQueries((prevData) => {
52+
const queryKeyIndex = prevData.findIndex(
53+
(item) => item.queryKey === queryKey
54+
);
55+
56+
return handleQueryDevtoolData(
57+
version,
58+
listenerType,
59+
queryKeyIndex,
60+
query,
61+
[...prevData]
62+
);
63+
});
64+
});
65+
66+
return () => {
67+
subscribe();
68+
};
69+
}, [queryClient, version]);
70+
71+
const selectedQuery = useMemo(
72+
() =>
73+
queries.find(
74+
(query) =>
75+
JSON.stringify(query?.queryKey) === JSON.stringify(selectedQueryKey)
76+
),
77+
[selectedQueryKey, queries]
78+
);
79+
80+
const filteredQueries = useMemo(
81+
() =>
82+
queries.filter(
83+
(query) =>
84+
query.queryKey
85+
?.toString()
86+
.toLowerCase()
87+
.includes(filter.toLowerCase())
88+
),
89+
[queries, filter]
90+
);
91+
92+
const handleSelectedRow = useCallback(
93+
(indexRow?: number) => {
94+
if (typeof indexRow !== 'number') {
95+
setSelectedQueryKey(undefined);
96+
return;
97+
}
98+
99+
const queriesSource = filter.length > 0 ? filteredQueries : queries;
100+
const queryKey = queriesSource[indexRow].queryKey;
101+
102+
setSelectedQueryKey((prev) => (prev !== queryKey ? queryKey : undefined));
103+
},
104+
[queries, filteredQueries, filter]
105+
);
106+
107+
return {
108+
selectedQueryKey,
109+
selectedQuery,
110+
filter,
111+
filteredQueries,
112+
setFilter,
113+
handleSelectedRow,
114+
};
115+
};
116+
117+
export default useDevtoolData;

0 commit comments

Comments
 (0)