|
| 1 | +--- |
| 2 | +slug: 14.7.18 |
| 3 | +title: 14.7.18 - useIAP Hook Improvements |
| 4 | +authors: [hyochan] |
| 5 | +tags: [release, hooks, performance, bugfix] |
| 6 | +date: 2026-03-25 |
| 7 | +--- |
| 8 | + |
| 9 | +# 14.7.18 Release Notes |
| 10 | + |
| 11 | +This release improves the `useIAP` hook with **stable function references**, **proper option forwarding**, and a new **`reconnect` method** for manual connection retry. |
| 12 | + |
| 13 | +<!-- truncate --> |
| 14 | + |
| 15 | +## Stable `restorePurchases` Reference ([#3172](https://github.com/hyochan/react-native-iap/pull/3172)) |
| 16 | + |
| 17 | +### Problem |
| 18 | + |
| 19 | +`restorePurchases` was defined as an inline async function in the return object, creating a new function reference on every render. This caused unnecessary re-renders in components that depended on it and broke `useCallback`/`useMemo` memoization chains. |
| 20 | + |
| 21 | +### Fix |
| 22 | + |
| 23 | +Extracted `restorePurchases` into a proper `useCallback` with a stable reference. It also now calls `syncIOS()` directly instead of going through `restorePurchasesTopLevel()`, eliminating a redundant `getAvailablePurchases` network request. |
| 24 | + |
| 25 | +```typescript |
| 26 | +// Before: new function every render + double network call |
| 27 | +restorePurchases: async () => { |
| 28 | + await restorePurchasesTopLevel(); // calls getAvailablePurchases internally |
| 29 | + await getAvailablePurchasesInternal(); // calls it again |
| 30 | +} |
| 31 | + |
| 32 | +// After: stable reference + single network call |
| 33 | +const restorePurchases = useCallback(async () => { |
| 34 | + if (Platform.OS === 'ios') await syncIOS(); |
| 35 | + await getAvailablePurchasesInternal(options); |
| 36 | +}, [getAvailablePurchasesInternal, invokeOnError]); |
| 37 | +``` |
| 38 | + |
| 39 | +## Forward `PurchaseOptions` in Hook Methods ([#3173](https://github.com/hyochan/react-native-iap/pull/3173)) |
| 40 | + |
| 41 | +### Problem |
| 42 | + |
| 43 | +The `useIAP` hook's `getAvailablePurchases` and `restorePurchases` methods didn't accept or forward `PurchaseOptions`. Users couldn't control iOS-specific options (`alsoPublishToEventListenerIOS`, `onlyIncludeActiveItemsIOS`) or Android options (`includeSuspendedAndroid`) through the hook API. |
| 44 | + |
| 45 | +### Fix |
| 46 | + |
| 47 | +Both methods now accept an optional `PurchaseOptions` parameter and forward it to the underlying API: |
| 48 | + |
| 49 | +```typescript |
| 50 | +const { getAvailablePurchases, restorePurchases } = useIAP(); |
| 51 | + |
| 52 | +// Now you can pass options through the hook |
| 53 | +await getAvailablePurchases({ |
| 54 | + onlyIncludeActiveItemsIOS: false, |
| 55 | + includeSuspendedAndroid: true, |
| 56 | +}); |
| 57 | + |
| 58 | +await restorePurchases({ |
| 59 | + alsoPublishToEventListenerIOS: true, |
| 60 | +}); |
| 61 | +``` |
| 62 | + |
| 63 | +The Android Nitro calls also now correctly forward `includeSuspendedAndroid` to the native layer. |
| 64 | + |
| 65 | +## New `reconnect` Method ([#3174](https://github.com/hyochan/react-native-iap/pull/3174)) |
| 66 | + |
| 67 | +### Problem |
| 68 | + |
| 69 | +When the initial store connection fails (e.g., Play Store not ready at mount time on Android, or transient network issues), users had no way to retry through the hook — they had to unmount and remount the component. |
| 70 | + |
| 71 | +### Fix |
| 72 | + |
| 73 | +Added a `reconnect()` method that manually retries the store connection: |
| 74 | + |
| 75 | +```typescript |
| 76 | +const { connected, reconnect } = useIAP({ |
| 77 | + onError: (error) => { |
| 78 | + console.warn('Connection failed:', error); |
| 79 | + }, |
| 80 | +}); |
| 81 | + |
| 82 | +// Retry connection after failure |
| 83 | +const handleRetry = async () => { |
| 84 | + const success = await reconnect(); |
| 85 | + if (success) { |
| 86 | + console.log('Reconnected!'); |
| 87 | + } |
| 88 | +}; |
| 89 | +``` |
| 90 | + |
| 91 | +Key implementation details: |
| 92 | + |
| 93 | +- **Shared helpers** — `buildAndroidConfig`, `registerListeners`, and `cleanupListeners` are extracted as shared `useCallback` helpers used by both `initIapWithSubscriptions` and `reconnect`, ensuring consistent behavior |
| 94 | +- **Safe state transitions** — `connected` is only set to `true` after listeners are successfully registered, preventing a "connected but no listeners" state |
| 95 | +- **Unmount guard** — `isMountedRef` check after async `initConnection` prevents listener leaks if the component unmounts during reconnection |
| 96 | +- **Full listener parity** — All listeners are re-registered including `userChoiceBillingAndroid` |
| 97 | + |
| 98 | +## Upgrade |
| 99 | + |
| 100 | +```bash |
| 101 | +yarn add react-native-iap@14.7.18 |
| 102 | +# or |
| 103 | +npm install react-native-iap@14.7.18 |
| 104 | +``` |
| 105 | + |
| 106 | +No breaking changes. The new `reconnect` method and `PurchaseOptions` parameter are additive. |
0 commit comments