Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 38 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# React Query External Sync

A powerful debugging tool for React Query state 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.
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.

Expand All @@ -9,6 +9,7 @@ Pairs perfectly with [React Native DevTools](https://github.com/LovesWorking/rn-
## ✨ 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
Expand Down Expand Up @@ -37,8 +38,10 @@ 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 for React Native or use other platform detection for web/desktop
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();
Expand All @@ -52,19 +55,30 @@ function App() {
}

function AppContent() {
// Set up the sync hook - automatically disabled in production!
// Unified storage queries and external sync - all in one hook!
useSyncQueriesExternal({
queryClient,
socketURL: "http://localhost:42831", // Default port for React Native DevTools
deviceName: Platform?.OS || "web", // Platform detection
platform: Platform?.OS || "web", // Use appropriate platform identifier
deviceId: Platform?.OS || "web", // Use a PERSISTENT identifier (see note below)
socketURL: "http://localhost:42831",
deviceName: Platform.OS,
platform: Platform.OS,
deviceId: Platform.OS,
extraDeviceInfo: {
// Optional additional info about your device
appVersion: "1.0.0",
// Add any relevant platform info
},
enableLogs: false,
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
Expand Down Expand Up @@ -107,15 +121,20 @@ For the best experience, use this package with the [React Native DevTools](https

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) |
| 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) |
| `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

Expand Down
2 changes: 2 additions & 0 deletions src/react-query-external-sync/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export interface User {
deviceName: string;
deviceId?: string; // Optional for backward compatibility
platform?: string; // Device platform (iOS, Android, Web)
extraDeviceInfo?: string; // json string of additional device information as key-value pairs
envVariables?: Record<string, string>; // Environment variables from the mobile app
}
23 changes: 23 additions & 0 deletions src/react-query-external-sync/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Storage Query Keys
export { storageQueryKeys } from './storageQueryKeys';

// Individual Storage Hooks
export {
type AsyncStorageQueryResult,
useDynamicAsyncStorageQueries,
type UseDynamicAsyncStorageQueriesOptions,
} from './useDynamicAsyncStorageQueries';
export {
type MmkvQueryResult,
type MmkvStorage,
useDynamicMmkvQueries,
type UseDynamicMmkvQueriesOptions,
} from './useDynamicMmkvQueries';
export {
type SecureStorageQueryResult,
useDynamicSecureStorageQueries,
type UseDynamicSecureStorageQueriesOptions,
} from './useDynamicSecureStorageQueries';

// Unified Storage Hook
export { type StorageQueryResults, useStorageQueries, type UseStorageQueriesOptions } from './useStorageQueries';
38 changes: 38 additions & 0 deletions src/react-query-external-sync/hooks/storageQueryKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Centralized storage query keys for all storage hooks
* This ensures consistency across MMKV, AsyncStorage, and SecureStorage hooks
* and allows easy modification of the base storage key in one place
*/
export const storageQueryKeys = {
/**
* Base storage key - change this to update all storage-related queries
*/
base: () => ['#storage'] as const,

/**
* MMKV storage query keys
*/
mmkv: {
root: () => [...storageQueryKeys.base(), 'mmkv'] as const,
key: (key: string) => [...storageQueryKeys.mmkv.root(), key] as const,
all: () => [...storageQueryKeys.mmkv.root(), 'all'] as const,
},

/**
* AsyncStorage query keys
*/
async: {
root: () => [...storageQueryKeys.base(), 'async'] as const,
key: (key: string) => [...storageQueryKeys.async.root(), key] as const,
all: () => [...storageQueryKeys.async.root(), 'all'] as const,
},

/**
* SecureStorage query keys
*/
secure: {
root: () => [...storageQueryKeys.base(), 'secure'] as const,
key: (key: string) => [...storageQueryKeys.secure.root(), key] as const,
all: () => [...storageQueryKeys.secure.root(), 'all'] as const,
},
} as const;
Loading