react-query-external-sync
Version:
A tool for syncing React Query state to an external Dev Tools
205 lines (157 loc) • 8.02 kB
Markdown
# React Query External Sync
A powerful debugging tool for React Query state and device storage in any React-based application. Whether you're building for mobile, web, desktop, TV, or VR - this package has you covered. It works seamlessly across all platforms where React runs, with zero configuration to disable in production.
Pairs perfectly with [React Native DevTools](https://github.com/LovesWorking/rn-better-dev-tools) for a complete development experience.

### If you need internal React Query dev tools within the device you can use my other package here!
https://github.com/LovesWorking/react-native-react-query-devtools
## ✨ Features
- 🔄 Real-time React Query state synchronization
- 💾 **Device storage monitoring with CRUD operations** - MMKV, AsyncStorage, and SecureStorage
- 📱 Works with any React-based framework (React, React Native, Expo, Next.js, etc.)
- 🖥️ Platform-agnostic: Web, iOS, Android, macOS, Windows, Linux, tvOS, VR - you name it!
- 🔌 Socket.IO integration for reliable communication
- 📊 Query status, data, and error monitoring
- ⚡️ Simple integration with minimal setup
- 🧩 Perfect companion to React Native DevTools
- 🛑 Zero-config production safety - automatically disabled in production builds
## 📦 Installation
```bash
# Using npm
npm install --save-dev react-query-external-sync socket.io-client
# Using yarn
yarn add -D react-query-external-sync socket.io-client
# Using pnpm
pnpm add -D react-query-external-sync socket.io-client
```
## 🚀 Quick Start
Add the hook to your application where you set up your React Query context:
```jsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useSyncQueriesExternal } from "react-query-external-sync";
import { Platform } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as SecureStore from "expo-secure-store";
import { storage } from "./mmkv"; // Your MMKV instance
// Create your query client
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<AppContent />
</QueryClientProvider>
);
}
function AppContent() {
// Unified storage queries and external sync - all in one hook!
useSyncQueriesExternal({
queryClient,
socketURL: "http://localhost:42831",
deviceName: Platform.OS,
platform: Platform.OS,
deviceId: Platform.OS,
extraDeviceInfo: {
appVersion: "1.0.0",
},
enableLogs: true,
envVariables: {
NODE_ENV: process.env.NODE_ENV,
},
// Storage monitoring with CRUD operations
mmkvStorage: storage, // MMKV storage for ['#storage', 'mmkv', 'key'] queries + monitoring
asyncStorage: AsyncStorage, // AsyncStorage for ['#storage', 'async', 'key'] queries + monitoring
secureStorage: SecureStore, // SecureStore for ['#storage', 'secure', 'key'] queries + monitoring
secureStorageKeys: [
"userToken",
"refreshToken",
"biometricKey",
"deviceId",
], // SecureStore keys to monitor
});
// Your app content
return <YourApp />;
}
```
## 🔒 Production Safety
This package is automatically disabled in production builds.
```jsx
// The package handles this internally:
if (process.env.NODE_ENV !== "production") {
useSyncQueries = require("./new-sync/useSyncQueries").useSyncQueries;
} else {
// In production, this becomes a no-op function
useSyncQueries = () => ({
isConnected: false,
connect: () => {},
disconnect: () => {},
socket: null,
users: [],
});
}
```
## 💡 Usage with DevTools
For the best experience, use this package with the [React Native DevTools](https://github.com/LovesWorking/rn-better-dev-tools) application:
1. Download and launch the DevTools application
2. Integrate this package in your React application
3. Start your application
4. DevTools will automatically detect and connect to your running application
> **Note**: For optimal connection, launch DevTools before starting your application.
## ⚙️ Configuration Options
The `useSyncQueriesExternal` hook accepts the following options:
| Option | Type | Required | Description |
| ------------------- | ------------ | -------- | ----------------------------------------------------------------------- |
| `queryClient` | QueryClient | Yes | Your React Query client instance |
| `socketURL` | string | Yes | URL of the socket server (e.g., 'http://localhost:42831') |
| `deviceName` | string | Yes | Human-readable name for your device |
| `platform` | string | Yes | Platform identifier ('ios', 'android', 'web', 'macos', 'windows', etc.) |
| `deviceId` | string | Yes | Unique identifier for your device |
| `extraDeviceInfo` | object | No | Additional device metadata to display in DevTools |
| `enableLogs` | boolean | No | Enable console logging for debugging (default: false) |
| `isDevice` | boolean | No | Set to true if this is a device (default: false) |
| `envVariables` | object | No | Environment variables to sync with DevTools |
| `mmkvStorage` | MmkvStorage | No | MMKV storage instance for real-time monitoring |
| `asyncStorage` | AsyncStorage | No | AsyncStorage instance for polling-based monitoring |
| `secureStorage` | SecureStore | No | SecureStore instance for secure data monitoring |
| `secureStorageKeys` | string[] | No | Array of SecureStore keys to monitor (required if using secureStorage) |
## 🐛 Troubleshooting
### Quick Checklist
1. **DevTools Connection**
- Look for "Connected" status in the top-left corner of the DevTools app
- If it shows "Disconnected", restart the DevTools app
2. **No Devices Appearing**
- Verify the Socket.IO client is installed (`npm list socket.io-client`)
- Ensure the hook is properly set up in your app
- Check that `socketURL` matches the DevTools port (default: 42831)
- Restart both your app and the DevTools
3. **Data Not Syncing**
- Confirm you're passing the correct `queryClient` instance
- Set `enableLogs: true` to see connection information
That's it! If you're still having issues, visit the [GitHub repository](https://github.com/LovesWorking/react-query-external-sync/issues) for support.
## ⚠️ Important Note About Device IDs
The `deviceId` parameter must be **persistent** across app restarts and re-renders. Using a value that changes (like `Date.now()`) will cause each render to be treated as a new device.
**Recommended approaches:**
```jsx
// Simple approach for single devices
deviceId: Platform.OS, // Works if you only have one device per platform
// Better approach for multiple simulators/devices of same type
// Using AsyncStorage, MMKV, or another storage solution
const [deviceId, setDeviceId] = useState(Platform.OS);
useEffect(() => {
const loadOrCreateDeviceId = async () => {
// Try to load existing ID
const storedId = await AsyncStorage.getItem('deviceId');
if (storedId) {
setDeviceId(storedId);
} else {
// First launch - generate and store a persistent ID
const newId = `${Platform.OS}-${Date.now()}`;
await AsyncStorage.setItem('deviceId', newId);
setDeviceId(newId);
}
};
loadOrCreateDeviceId();
}, []);
```
## 📄 License
MIT
---
Made with ❤️ by [LovesWorking](https://github.com/LovesWorking)