@mustafaaksoy41/react-native-offline-queue 0.1.2 → 0.1.4
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 +204 -20
- package/lib/commonjs/hooks/useOfflineMutation.js +29 -6
- package/lib/commonjs/hooks/useOfflineMutation.js.map +1 -1
- package/lib/module/hooks/useOfflineMutation.js +30 -7
- package/lib/module/hooks/useOfflineMutation.js.map +1 -1
- package/lib/typescript/hooks/useOfflineMutation.d.ts +14 -3
- package/lib/typescript/hooks/useOfflineMutation.d.ts.map +1 -1
- package/package.json +13 -3
- package/src/hooks/useOfflineMutation.ts +50 -8
package/README.md
CHANGED
|
@@ -3,21 +3,22 @@
|
|
|
3
3
|
# 📡 react-native-offline-queue
|
|
4
4
|
|
|
5
5
|
**A lightweight, high-performance offline queue and sync manager for React Native.**
|
|
6
|
-
Queue operations when offline, sync automatically or manually when connectivity returns.
|
|
6
|
+
Queue operations when offline, sync automatically or manually when connectivity returns. Works great with React Query (TanStack Query).
|
|
7
7
|
|
|
8
8
|
<br />
|
|
9
9
|
|
|
10
10
|
<!-- Package Info -->
|
|
11
11
|
[](https://www.npmjs.com/package/@mustafaaksoy41/react-native-offline-queue)
|
|
12
12
|
[](https://www.npmjs.com/package/@mustafaaksoy41/react-native-offline-queue)
|
|
13
|
-
[](https://github.com/
|
|
14
|
-
[](https://github.com/messivite/react-native-offline-queue/blob/main/LICENSE)
|
|
14
|
+
[](https://bundlephobia.com/package/@mustafaaksoy41/react-native-offline-queue)
|
|
15
15
|
|
|
16
16
|
<!-- Platform & Language -->
|
|
17
17
|
[](https://reactnative.dev/)
|
|
18
18
|
[](https://reactnative.dev/)
|
|
19
19
|
[](https://www.typescriptlang.org/)
|
|
20
20
|
[](https://reactnative.dev/)
|
|
21
|
+
[](https://tanstack.com/query/latest)
|
|
21
22
|
|
|
22
23
|
<!-- Supported Storage Adapters -->
|
|
23
24
|
[](https://github.com/mrousavy/react-native-mmkv)
|
|
@@ -185,6 +186,46 @@ function LikeButton({ postId }) {
|
|
|
185
186
|
- **Offline**: The action is saved to the queue, and `onOptimisticSuccess` fires so the UI updates instantly.
|
|
186
187
|
- **When connectivity returns**: Queued actions are synced — per-action handler first, then `onSyncAction` as fallback.
|
|
187
188
|
|
|
189
|
+
### How API requests work (real URLs)
|
|
190
|
+
|
|
191
|
+
The `handler` is where you make the **actual API call** — `fetch`, axios, or your API client. When the user presses a button:
|
|
192
|
+
|
|
193
|
+
1. **Online**: `handler(payload)` runs immediately → your `fetch('https://api.myapp.com/...')` fires right away.
|
|
194
|
+
2. **Offline**: `{ actionName, payload }` is stored in the queue. When connectivity returns, the queue flushes and `handler(payload)` runs for each item → your real API requests are sent.
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
function CreatePostScreen() {
|
|
198
|
+
const { mutateOffline } = useOfflineMutation('CREATE_POST', {
|
|
199
|
+
handler: async (payload) => {
|
|
200
|
+
// Your real API URL — runs immediately when online, or after queue flushes when offline
|
|
201
|
+
const res = await fetch('https://api.myapp.com/v1/posts', {
|
|
202
|
+
method: 'POST',
|
|
203
|
+
headers: {
|
|
204
|
+
'Content-Type': 'application/json',
|
|
205
|
+
'Authorization': `Bearer ${await getAuthToken()}`,
|
|
206
|
+
},
|
|
207
|
+
body: JSON.stringify(payload),
|
|
208
|
+
});
|
|
209
|
+
if (!res.ok) throw new Error(await res.text());
|
|
210
|
+
},
|
|
211
|
+
onOptimisticSuccess: () => navigation.goBack(),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<Button
|
|
216
|
+
title="Submit"
|
|
217
|
+
onPress={() => mutateOffline({ title, body })}
|
|
218
|
+
/>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
| User action | Network | What happens |
|
|
224
|
+
|-------------|---------|--------------|
|
|
225
|
+
| Button press | Online | `handler` runs → `fetch` fires → API receives request |
|
|
226
|
+
| Button press | Offline | Action queued, `onOptimisticSuccess` fires, UI updates |
|
|
227
|
+
| Connectivity restores | — | Queue flushes → each `handler` runs → real API calls sent |
|
|
228
|
+
|
|
188
229
|
### Full Example
|
|
189
230
|
|
|
190
231
|
Here's what a real app looks like with multiple offline-capable actions. Each component owns its own API logic — no central switch-case needed.
|
|
@@ -264,6 +305,86 @@ function MessageBubble({ chatId }) {
|
|
|
264
305
|
|
|
265
306
|
Each `handler` is self-contained: when the user goes offline, actions are queued with their `actionName`. When connectivity returns, the queue flushes and each action runs through its registered handler automatically.
|
|
266
307
|
|
|
308
|
+
### Using with React Query (TanStack Query)
|
|
309
|
+
|
|
310
|
+
Use your **existing `useMutation` hook** — the handler calls `mutateAsync`. No duplicate fetch, no extra API layer. Your mutation does everything (POST, cache invalidation, etc.).
|
|
311
|
+
|
|
312
|
+
**Pattern — Handler uses `mutateAsync` from `useMutation`**
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
316
|
+
import { useOfflineMutation } from '@mustafaaksoy41/react-native-offline-queue';
|
|
317
|
+
|
|
318
|
+
function CreatePostForm() {
|
|
319
|
+
const queryClient = useQueryClient();
|
|
320
|
+
|
|
321
|
+
// Your existing React Query mutation — mutationFn, onSuccess, retry, etc.
|
|
322
|
+
const { mutateAsync } = useMutation({
|
|
323
|
+
mutationFn: (payload: { title: string; body: string }) =>
|
|
324
|
+
fetch('https://api.myapp.com/posts', {
|
|
325
|
+
method: 'POST',
|
|
326
|
+
headers: { 'Content-Type': 'application/json' },
|
|
327
|
+
body: JSON.stringify(payload),
|
|
328
|
+
}).then((r) => r.json()),
|
|
329
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Handler = your mutation. Online: runs immediately. Offline: queued, runs when back online.
|
|
333
|
+
const { mutateOffline } = useOfflineMutation('CREATE_POST', {
|
|
334
|
+
handler: async (payload) => {
|
|
335
|
+
await mutateAsync(payload);
|
|
336
|
+
},
|
|
337
|
+
onOptimisticSuccess: (payload) => {
|
|
338
|
+
queryClient.setQueryData(['posts'], (old: any) =>
|
|
339
|
+
old ? [...old, { ...payload, id: 'temp', pending: true }] : [payload]
|
|
340
|
+
);
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
return (
|
|
345
|
+
<Button
|
|
346
|
+
title="Post"
|
|
347
|
+
onPress={() => mutateOffline({ title, body })}
|
|
348
|
+
/>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**With custom hooks / API layer**
|
|
354
|
+
|
|
355
|
+
If your mutations live in custom hooks:
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
// hooks/useCreatePost.ts
|
|
359
|
+
export function useCreatePost() {
|
|
360
|
+
const queryClient = useQueryClient();
|
|
361
|
+
return useMutation({
|
|
362
|
+
mutationFn: api.createPost, // your axios/fetch wrapper
|
|
363
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// CreatePostForm.tsx
|
|
368
|
+
function CreatePostForm() {
|
|
369
|
+
const { mutateAsync } = useCreatePost();
|
|
370
|
+
|
|
371
|
+
const { mutateOffline } = useOfflineMutation('CREATE_POST', {
|
|
372
|
+
handler: async (payload) => await mutateAsync(payload),
|
|
373
|
+
onOptimisticSuccess: (payload) => { /* ... */ },
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
return <Button onPress={() => mutateOffline({ title, body })} />;
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Summary**
|
|
381
|
+
|
|
382
|
+
| Handler does | Your mutation does |
|
|
383
|
+
|--------------|--------------------|
|
|
384
|
+
| Calls `mutateAsync(payload)` | POST/GET, retry, cache invalidation, error handling |
|
|
385
|
+
|
|
386
|
+
No duplicate logic. Same mutation for online and offline sync.
|
|
387
|
+
|
|
267
388
|
## Configuration
|
|
268
389
|
|
|
269
390
|
```tsx
|
|
@@ -354,31 +475,94 @@ Omit `onOnlineRestore` entirely. Nothing happens — you handle sync manually th
|
|
|
354
475
|
|
|
355
476
|
### `useOfflineMutation(actionName, options?)`
|
|
356
477
|
|
|
357
|
-
Queue-aware mutation hook. Calls the handler directly when online, queues when offline.
|
|
478
|
+
Queue-aware mutation hook with built-in state tracking. Calls the handler directly when online, queues when offline.
|
|
358
479
|
|
|
359
|
-
|
|
360
|
-
const { mutateOffline } = useOfflineMutation('CREATE_POST', {
|
|
361
|
-
handler: async (payload) => {
|
|
362
|
-
// Your API call — runs directly when online, or during sync when offline
|
|
363
|
-
await api.createPost(payload);
|
|
364
|
-
},
|
|
365
|
-
onOptimisticSuccess: (payload) => {
|
|
366
|
-
// Runs immediately — update your local state here
|
|
367
|
-
},
|
|
368
|
-
onError: (error, payload) => {
|
|
369
|
-
// Runs if the direct API call fails while online
|
|
370
|
-
},
|
|
371
|
-
});
|
|
480
|
+
**Returns:**
|
|
372
481
|
|
|
373
|
-
|
|
482
|
+
```tsx
|
|
483
|
+
const {
|
|
484
|
+
mutateOffline, // (payload) => Promise<void>
|
|
485
|
+
status, // 'idle' | 'loading' | 'success' | 'error' | 'queued'
|
|
486
|
+
isIdle, // true before any mutation
|
|
487
|
+
isLoading, // true while the handler is running (online only)
|
|
488
|
+
isSuccess, // true after a successful direct call
|
|
489
|
+
isError, // true if the direct call threw (action still queued as fallback)
|
|
490
|
+
isQueued, // true when the action was added to the offline queue
|
|
491
|
+
error, // Error | null
|
|
492
|
+
reset, // () => void — reset status back to idle
|
|
493
|
+
} = useOfflineMutation('ACTION_NAME', options);
|
|
374
494
|
```
|
|
375
495
|
|
|
376
496
|
| Option | Type | Description |
|
|
377
497
|
|--------|------|-------------|
|
|
378
|
-
| `handler` | `(payload) => Promise<void>` | API call for this
|
|
379
|
-
| `onOptimisticSuccess` | `(payload) => void` | Fires immediately
|
|
498
|
+
| `handler` | `(payload) => Promise<void>` | API call for this action. Registered automatically, used during sync. |
|
|
499
|
+
| `onOptimisticSuccess` | `(payload) => void` | Fires immediately — update your local state here |
|
|
500
|
+
| `onSuccess` | `(payload) => void` | Fires only after a successful direct call (online) |
|
|
380
501
|
| `onError` | `(error, payload) => void` | Fires if the direct call fails while online |
|
|
381
502
|
|
|
503
|
+
**With `fetch`:**
|
|
504
|
+
|
|
505
|
+
```tsx
|
|
506
|
+
function LikeButton({ postId }) {
|
|
507
|
+
const { mutateOffline, isLoading, isQueued } = useOfflineMutation('LIKE_POST', {
|
|
508
|
+
handler: async (payload) => {
|
|
509
|
+
await fetch('/api/likes', {
|
|
510
|
+
method: 'POST',
|
|
511
|
+
body: JSON.stringify(payload),
|
|
512
|
+
});
|
|
513
|
+
},
|
|
514
|
+
onOptimisticSuccess: () => setLiked(true),
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
return (
|
|
518
|
+
<Button
|
|
519
|
+
title={isLoading ? '⏳' : isQueued ? '📡 Queued' : '❤️ Like'}
|
|
520
|
+
onPress={() => mutateOffline({ postId })}
|
|
521
|
+
disabled={isLoading}
|
|
522
|
+
/>
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
**With React Query:**
|
|
528
|
+
|
|
529
|
+
```tsx
|
|
530
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
531
|
+
|
|
532
|
+
function LikeButton({ postId }) {
|
|
533
|
+
const queryClient = useQueryClient();
|
|
534
|
+
|
|
535
|
+
const { mutateAsync } = useMutation({
|
|
536
|
+
mutationFn: (payload) => api.likePost(payload),
|
|
537
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
const { mutateOffline, isQueued } = useOfflineMutation('LIKE_POST', {
|
|
541
|
+
handler: async (payload) => await mutateAsync(payload),
|
|
542
|
+
onOptimisticSuccess: () => {
|
|
543
|
+
queryClient.setQueryData(['posts', postId], (old) => ({
|
|
544
|
+
...old, liked: true,
|
|
545
|
+
}));
|
|
546
|
+
},
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
return (
|
|
550
|
+
<Button
|
|
551
|
+
title={isQueued ? '📡 Queued' : '❤️ Like'}
|
|
552
|
+
onPress={() => mutateOffline({ postId })}
|
|
553
|
+
/>
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
**State flow:**
|
|
559
|
+
|
|
560
|
+
| Scenario | `status` flow |
|
|
561
|
+
|----------|---------------|
|
|
562
|
+
| Online + success | `idle` → `loading` → `success` |
|
|
563
|
+
| Online + API fails | `idle` → `loading` → `queued` (fallback) |
|
|
564
|
+
| Offline | `idle` → `queued` |
|
|
565
|
+
|
|
382
566
|
### `useOfflineQueue()`
|
|
383
567
|
|
|
384
568
|
Access the live queue state. Uses `useSyncExternalStore` under the hood — only re-renders when the queue actually changes.
|
|
@@ -13,6 +13,8 @@ function useOfflineMutation(actionName, options) {
|
|
|
13
13
|
} = (0, _OfflineProvider.useNetworkStatus)();
|
|
14
14
|
const handlerRef = (0, _react.useRef)(options?.handler);
|
|
15
15
|
handlerRef.current = options?.handler;
|
|
16
|
+
const [status, setStatus] = (0, _react.useState)('idle');
|
|
17
|
+
const [error, setError] = (0, _react.useState)(null);
|
|
16
18
|
|
|
17
19
|
// Register per-action handler (persists even after unmount)
|
|
18
20
|
(0, _react.useEffect)(() => {
|
|
@@ -20,7 +22,11 @@ function useOfflineMutation(actionName, options) {
|
|
|
20
22
|
_OfflineManager.OfflineManager.registerHandler(actionName, payload => handlerRef.current(payload));
|
|
21
23
|
}
|
|
22
24
|
}, [actionName]);
|
|
23
|
-
const
|
|
25
|
+
const reset = (0, _react.useCallback)(() => {
|
|
26
|
+
setStatus('idle');
|
|
27
|
+
setError(null);
|
|
28
|
+
}, []);
|
|
29
|
+
const mutateOffline = (0, _react.useCallback)(async payload => {
|
|
24
30
|
// Resolve which handler to use: per-action handler > global onSyncAction
|
|
25
31
|
const handler = handlerRef.current || _OfflineManager.OfflineManager.getHandler(actionName);
|
|
26
32
|
const globalHandler = _OfflineManager.OfflineManager.onSyncAction;
|
|
@@ -28,6 +34,8 @@ function useOfflineMutation(actionName, options) {
|
|
|
28
34
|
if (isOnline && hasHandler) {
|
|
29
35
|
// ── ONLINE: Execute directly, skip the queue ──
|
|
30
36
|
if (__DEV__) console.log(`[OfflineQueue] mutate: ${actionName} (direct)`);
|
|
37
|
+
setStatus('loading');
|
|
38
|
+
setError(null);
|
|
31
39
|
try {
|
|
32
40
|
if (handler) {
|
|
33
41
|
await handler(payload);
|
|
@@ -40,22 +48,37 @@ function useOfflineMutation(actionName, options) {
|
|
|
40
48
|
retryCount: 0
|
|
41
49
|
});
|
|
42
50
|
}
|
|
51
|
+
setStatus('success');
|
|
43
52
|
options?.onOptimisticSuccess?.(payload);
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
options?.onSuccess?.(payload);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
console.warn(`[OfflineQueue] mutate: ${actionName} failed, falling back to queue`, err);
|
|
56
|
+
// API failed even though online → fallback to queue
|
|
46
57
|
await _OfflineManager.OfflineManager.push(actionName, payload);
|
|
58
|
+
setStatus('queued');
|
|
59
|
+
setError(err);
|
|
47
60
|
options?.onOptimisticSuccess?.(payload);
|
|
48
|
-
options?.onError?.(
|
|
61
|
+
options?.onError?.(err, payload);
|
|
49
62
|
}
|
|
50
63
|
} else {
|
|
51
64
|
// ── OFFLINE: Add to queue + optimistic update ──
|
|
52
65
|
if (__DEV__) console.log(`[OfflineQueue] mutate: ${actionName} (queued)`);
|
|
53
66
|
await _OfflineManager.OfflineManager.push(actionName, payload);
|
|
67
|
+
setStatus('queued');
|
|
68
|
+
setError(null);
|
|
54
69
|
options?.onOptimisticSuccess?.(payload);
|
|
55
70
|
}
|
|
56
|
-
};
|
|
71
|
+
}, [actionName, isOnline, options]);
|
|
57
72
|
return {
|
|
58
|
-
mutateOffline
|
|
73
|
+
mutateOffline,
|
|
74
|
+
status,
|
|
75
|
+
isIdle: status === 'idle',
|
|
76
|
+
isLoading: status === 'loading',
|
|
77
|
+
isSuccess: status === 'success',
|
|
78
|
+
isError: status === 'error',
|
|
79
|
+
isQueued: status === 'queued',
|
|
80
|
+
error,
|
|
81
|
+
reset
|
|
59
82
|
};
|
|
60
83
|
}
|
|
61
84
|
//# sourceMappingURL=useOfflineMutation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","_OfflineManager","_OfflineProvider","useOfflineMutation","actionName","options","isOnline","useNetworkStatus","handlerRef","useRef","handler","current","useEffect","OfflineManager","registerHandler","payload","mutateOffline","getHandler","globalHandler","onSyncAction","hasHandler","__DEV__","console","log","id","createdAt","Date","now","retryCount","onOptimisticSuccess","
|
|
1
|
+
{"version":3,"names":["_react","require","_OfflineManager","_OfflineProvider","useOfflineMutation","actionName","options","isOnline","useNetworkStatus","handlerRef","useRef","handler","current","status","setStatus","useState","error","setError","useEffect","OfflineManager","registerHandler","payload","reset","useCallback","mutateOffline","getHandler","globalHandler","onSyncAction","hasHandler","__DEV__","console","log","id","createdAt","Date","now","retryCount","onOptimisticSuccess","onSuccess","err","warn","push","onError","isIdle","isLoading","isSuccess","isError","isQueued"],"sourceRoot":"../../../src","sources":["hooks/useOfflineMutation.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { useEffect, useRef, useState, useCallback } from 'react';
|
|
4
4
|
import { OfflineManager } from '../core/OfflineManager';
|
|
5
5
|
import { useNetworkStatus } from '../components/OfflineProvider';
|
|
6
6
|
export function useOfflineMutation(actionName, options) {
|
|
@@ -9,6 +9,8 @@ export function useOfflineMutation(actionName, options) {
|
|
|
9
9
|
} = useNetworkStatus();
|
|
10
10
|
const handlerRef = useRef(options?.handler);
|
|
11
11
|
handlerRef.current = options?.handler;
|
|
12
|
+
const [status, setStatus] = useState('idle');
|
|
13
|
+
const [error, setError] = useState(null);
|
|
12
14
|
|
|
13
15
|
// Register per-action handler (persists even after unmount)
|
|
14
16
|
useEffect(() => {
|
|
@@ -16,7 +18,11 @@ export function useOfflineMutation(actionName, options) {
|
|
|
16
18
|
OfflineManager.registerHandler(actionName, payload => handlerRef.current(payload));
|
|
17
19
|
}
|
|
18
20
|
}, [actionName]);
|
|
19
|
-
const
|
|
21
|
+
const reset = useCallback(() => {
|
|
22
|
+
setStatus('idle');
|
|
23
|
+
setError(null);
|
|
24
|
+
}, []);
|
|
25
|
+
const mutateOffline = useCallback(async payload => {
|
|
20
26
|
// Resolve which handler to use: per-action handler > global onSyncAction
|
|
21
27
|
const handler = handlerRef.current || OfflineManager.getHandler(actionName);
|
|
22
28
|
const globalHandler = OfflineManager.onSyncAction;
|
|
@@ -24,6 +30,8 @@ export function useOfflineMutation(actionName, options) {
|
|
|
24
30
|
if (isOnline && hasHandler) {
|
|
25
31
|
// ── ONLINE: Execute directly, skip the queue ──
|
|
26
32
|
if (__DEV__) console.log(`[OfflineQueue] mutate: ${actionName} (direct)`);
|
|
33
|
+
setStatus('loading');
|
|
34
|
+
setError(null);
|
|
27
35
|
try {
|
|
28
36
|
if (handler) {
|
|
29
37
|
await handler(payload);
|
|
@@ -36,22 +44,37 @@ export function useOfflineMutation(actionName, options) {
|
|
|
36
44
|
retryCount: 0
|
|
37
45
|
});
|
|
38
46
|
}
|
|
47
|
+
setStatus('success');
|
|
39
48
|
options?.onOptimisticSuccess?.(payload);
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
options?.onSuccess?.(payload);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.warn(`[OfflineQueue] mutate: ${actionName} failed, falling back to queue`, err);
|
|
52
|
+
// API failed even though online → fallback to queue
|
|
42
53
|
await OfflineManager.push(actionName, payload);
|
|
54
|
+
setStatus('queued');
|
|
55
|
+
setError(err);
|
|
43
56
|
options?.onOptimisticSuccess?.(payload);
|
|
44
|
-
options?.onError?.(
|
|
57
|
+
options?.onError?.(err, payload);
|
|
45
58
|
}
|
|
46
59
|
} else {
|
|
47
60
|
// ── OFFLINE: Add to queue + optimistic update ──
|
|
48
61
|
if (__DEV__) console.log(`[OfflineQueue] mutate: ${actionName} (queued)`);
|
|
49
62
|
await OfflineManager.push(actionName, payload);
|
|
63
|
+
setStatus('queued');
|
|
64
|
+
setError(null);
|
|
50
65
|
options?.onOptimisticSuccess?.(payload);
|
|
51
66
|
}
|
|
52
|
-
};
|
|
67
|
+
}, [actionName, isOnline, options]);
|
|
53
68
|
return {
|
|
54
|
-
mutateOffline
|
|
69
|
+
mutateOffline,
|
|
70
|
+
status,
|
|
71
|
+
isIdle: status === 'idle',
|
|
72
|
+
isLoading: status === 'loading',
|
|
73
|
+
isSuccess: status === 'success',
|
|
74
|
+
isError: status === 'error',
|
|
75
|
+
isQueued: status === 'queued',
|
|
76
|
+
error,
|
|
77
|
+
reset
|
|
55
78
|
};
|
|
56
79
|
}
|
|
57
80
|
//# sourceMappingURL=useOfflineMutation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useEffect","useRef","OfflineManager","useNetworkStatus","useOfflineMutation","actionName","options","isOnline","handlerRef","handler","current","registerHandler","payload","mutateOffline","getHandler","globalHandler","onSyncAction","hasHandler","__DEV__","console","log","id","createdAt","Date","now","retryCount","onOptimisticSuccess","
|
|
1
|
+
{"version":3,"names":["useEffect","useRef","useState","useCallback","OfflineManager","useNetworkStatus","useOfflineMutation","actionName","options","isOnline","handlerRef","handler","current","status","setStatus","error","setError","registerHandler","payload","reset","mutateOffline","getHandler","globalHandler","onSyncAction","hasHandler","__DEV__","console","log","id","createdAt","Date","now","retryCount","onOptimisticSuccess","onSuccess","err","warn","push","onError","isIdle","isLoading","isSuccess","isError","isQueued"],"sourceRoot":"../../../src","sources":["hooks/useOfflineMutation.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
export type MutationStatus = 'idle' | 'loading' | 'success' | 'error' | 'queued';
|
|
2
|
+
export interface OfflineMutationResult<TPayload> {
|
|
3
|
+
mutateOffline: (payload: TPayload) => Promise<void>;
|
|
4
|
+
status: MutationStatus;
|
|
5
|
+
isIdle: boolean;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
isSuccess: boolean;
|
|
8
|
+
isError: boolean;
|
|
9
|
+
isQueued: boolean;
|
|
10
|
+
error: Error | null;
|
|
11
|
+
reset: () => void;
|
|
12
|
+
}
|
|
1
13
|
export declare function useOfflineMutation<TPayload>(actionName: string, options?: {
|
|
2
14
|
handler?: (payload: TPayload) => Promise<void>;
|
|
3
15
|
onOptimisticSuccess?: (payload: TPayload) => void;
|
|
4
16
|
onError?: (error: Error, payload: TPayload) => void;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
17
|
+
onSuccess?: (payload: TPayload) => void;
|
|
18
|
+
}): OfflineMutationResult<TPayload>;
|
|
8
19
|
//# sourceMappingURL=useOfflineMutation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOfflineMutation.d.ts","sourceRoot":"","sources":["../../../src/hooks/useOfflineMutation.ts"],"names":[],"mappings":"AAIA,wBAAgB,kBAAkB,CAAC,QAAQ,EACzC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"useOfflineMutation.d.ts","sourceRoot":"","sources":["../../../src/hooks/useOfflineMutation.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEjF,MAAM,WAAW,qBAAqB,CAAC,QAAQ;IAC7C,aAAa,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EACzC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;CACzC,GACA,qBAAqB,CAAC,QAAQ,CAAC,CA8EjC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mustafaaksoy41/react-native-offline-queue",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "A flexible, high-performance offline queue and synchronizer for React Native.",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "A flexible, high-performance offline queue and synchronizer for React Native. Works great with React Query (TanStack Query).",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
7
7
|
"types": "lib/typescript/index.d.ts",
|
|
@@ -23,10 +23,20 @@
|
|
|
23
23
|
"offline",
|
|
24
24
|
"queue",
|
|
25
25
|
"sync",
|
|
26
|
-
"hook"
|
|
26
|
+
"hook",
|
|
27
|
+
"react-query",
|
|
28
|
+
"tanstack-query"
|
|
27
29
|
],
|
|
28
30
|
"author": "Mustafa Aksoy",
|
|
29
31
|
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/messivite/react-native-offline-queue.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/messivite/react-native-offline-queue#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/messivite/react-native-offline-queue/issues"
|
|
39
|
+
},
|
|
30
40
|
"peerDependencies": {
|
|
31
41
|
"@react-native-async-storage/async-storage": "*",
|
|
32
42
|
"@react-native-community/netinfo": "*",
|
|
@@ -1,19 +1,37 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
1
|
+
import { useEffect, useRef, useState, useCallback } from 'react';
|
|
2
2
|
import { OfflineManager } from '../core/OfflineManager';
|
|
3
3
|
import { useNetworkStatus } from '../components/OfflineProvider';
|
|
4
4
|
|
|
5
|
+
export type MutationStatus = 'idle' | 'loading' | 'success' | 'error' | 'queued';
|
|
6
|
+
|
|
7
|
+
export interface OfflineMutationResult<TPayload> {
|
|
8
|
+
mutateOffline: (payload: TPayload) => Promise<void>;
|
|
9
|
+
status: MutationStatus;
|
|
10
|
+
isIdle: boolean;
|
|
11
|
+
isLoading: boolean;
|
|
12
|
+
isSuccess: boolean;
|
|
13
|
+
isError: boolean;
|
|
14
|
+
isQueued: boolean;
|
|
15
|
+
error: Error | null;
|
|
16
|
+
reset: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
5
19
|
export function useOfflineMutation<TPayload>(
|
|
6
20
|
actionName: string,
|
|
7
21
|
options?: {
|
|
8
22
|
handler?: (payload: TPayload) => Promise<void>;
|
|
9
23
|
onOptimisticSuccess?: (payload: TPayload) => void;
|
|
10
24
|
onError?: (error: Error, payload: TPayload) => void;
|
|
25
|
+
onSuccess?: (payload: TPayload) => void;
|
|
11
26
|
}
|
|
12
|
-
) {
|
|
27
|
+
): OfflineMutationResult<TPayload> {
|
|
13
28
|
const { isOnline } = useNetworkStatus();
|
|
14
29
|
const handlerRef = useRef(options?.handler);
|
|
15
30
|
handlerRef.current = options?.handler;
|
|
16
31
|
|
|
32
|
+
const [status, setStatus] = useState<MutationStatus>('idle');
|
|
33
|
+
const [error, setError] = useState<Error | null>(null);
|
|
34
|
+
|
|
17
35
|
// Register per-action handler (persists even after unmount)
|
|
18
36
|
useEffect(() => {
|
|
19
37
|
if (handlerRef.current) {
|
|
@@ -23,7 +41,12 @@ export function useOfflineMutation<TPayload>(
|
|
|
23
41
|
}
|
|
24
42
|
}, [actionName]);
|
|
25
43
|
|
|
26
|
-
const
|
|
44
|
+
const reset = useCallback(() => {
|
|
45
|
+
setStatus('idle');
|
|
46
|
+
setError(null);
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
const mutateOffline = useCallback(async (payload: TPayload) => {
|
|
27
50
|
// Resolve which handler to use: per-action handler > global onSyncAction
|
|
28
51
|
const handler = handlerRef.current || OfflineManager.getHandler(actionName);
|
|
29
52
|
const globalHandler = OfflineManager.onSyncAction;
|
|
@@ -32,6 +55,8 @@ export function useOfflineMutation<TPayload>(
|
|
|
32
55
|
if (isOnline && hasHandler) {
|
|
33
56
|
// ── ONLINE: Execute directly, skip the queue ──
|
|
34
57
|
if (__DEV__) console.log(`[OfflineQueue] mutate: ${actionName} (direct)`);
|
|
58
|
+
setStatus('loading');
|
|
59
|
+
setError(null);
|
|
35
60
|
try {
|
|
36
61
|
if (handler) {
|
|
37
62
|
await handler(payload);
|
|
@@ -44,20 +69,37 @@ export function useOfflineMutation<TPayload>(
|
|
|
44
69
|
retryCount: 0,
|
|
45
70
|
});
|
|
46
71
|
}
|
|
72
|
+
setStatus('success');
|
|
47
73
|
options?.onOptimisticSuccess?.(payload);
|
|
48
|
-
|
|
49
|
-
|
|
74
|
+
options?.onSuccess?.(payload);
|
|
75
|
+
} catch (err: any) {
|
|
76
|
+
console.warn(`[OfflineQueue] mutate: ${actionName} failed, falling back to queue`, err);
|
|
77
|
+
// API failed even though online → fallback to queue
|
|
50
78
|
await OfflineManager.push(actionName, payload);
|
|
79
|
+
setStatus('queued');
|
|
80
|
+
setError(err);
|
|
51
81
|
options?.onOptimisticSuccess?.(payload);
|
|
52
|
-
options?.onError?.(
|
|
82
|
+
options?.onError?.(err, payload);
|
|
53
83
|
}
|
|
54
84
|
} else {
|
|
55
85
|
// ── OFFLINE: Add to queue + optimistic update ──
|
|
56
86
|
if (__DEV__) console.log(`[OfflineQueue] mutate: ${actionName} (queued)`);
|
|
57
87
|
await OfflineManager.push(actionName, payload);
|
|
88
|
+
setStatus('queued');
|
|
89
|
+
setError(null);
|
|
58
90
|
options?.onOptimisticSuccess?.(payload);
|
|
59
91
|
}
|
|
60
|
-
};
|
|
92
|
+
}, [actionName, isOnline, options]);
|
|
61
93
|
|
|
62
|
-
return {
|
|
94
|
+
return {
|
|
95
|
+
mutateOffline,
|
|
96
|
+
status,
|
|
97
|
+
isIdle: status === 'idle',
|
|
98
|
+
isLoading: status === 'loading',
|
|
99
|
+
isSuccess: status === 'success',
|
|
100
|
+
isError: status === 'error',
|
|
101
|
+
isQueued: status === 'queued',
|
|
102
|
+
error,
|
|
103
|
+
reset,
|
|
104
|
+
};
|
|
63
105
|
}
|