feat(rn-window): auto-recover on sendAction when WebView handshake failed#1599
feat(rn-window): auto-recover on sendAction when WebView handshake failed#1599panosinthezone wants to merge 3 commits intomainfrom
Conversation
…iled When the React Native WebView handshake fails (timeout or error), subsequent sendAction calls now automatically reload the WebView and retry the handshake before sending the action. This makes the RN path self-healing, similar to how the web iframe path re-creates the iframe on failure. Co-Authored-By: Panayiotis Halios <panos@paella.dev>
🦋 Changeset detectedLatest commit: c5e3619 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Original prompt from PanayiotisNote: 2.
|
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Co-Authored-By: Panayiotis Halios <panos@paella.dev>
| if (!this.isConnected) { | ||
| console.info("[WebViewParent] Not connected, reloading and re-establishing handshake before sendAction"); | ||
| await this.reloadAndHandshake(); | ||
| } |
There was a problem hiding this comment.
Race condition during initial WebView load: if sendAction() is called after onWebViewLoad sets isConnected = false but before handshakeWithChild() completes, this will trigger an unnecessary reload.
The initial handshake is already in progress (via _ongoingHandshakeWithChild), but reloadAndHandshake() uses a separate single-flight guard (_reconnectFlight), so it will reload the WebView mid-handshake.
Consider checking if a handshake is already in progress before reloading:
| if (!this.isConnected) { | |
| console.info("[WebViewParent] Not connected, reloading and re-establishing handshake before sendAction"); | |
| await this.reloadAndHandshake(); | |
| } | |
| if (!this.isConnected && this._ongoingHandshakeWithChild == null) { | |
| console.info("[WebViewParent] Not connected, reloading and re-establishing handshake before sendAction"); | |
| await this.reloadAndHandshake(); | |
| } else if (!this.isConnected) { | |
| console.info("[WebViewParent] Handshake already in progress, waiting for it to complete"); | |
| await this.handshakeWithChild(); | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/client/rn-window/src/rn-webview/Parent.ts
Line: 105-108
Comment:
Race condition during initial WebView load: if `sendAction()` is called after `onWebViewLoad` sets `isConnected = false` but before `handshakeWithChild()` completes, this will trigger an unnecessary reload.
The initial handshake is already in progress (via `_ongoingHandshakeWithChild`), but `reloadAndHandshake()` uses a separate single-flight guard (`_reconnectFlight`), so it will reload the WebView mid-handshake.
Consider checking if a handshake is already in progress before reloading:
```suggestion
if (!this.isConnected && this._ongoingHandshakeWithChild == null) {
console.info("[WebViewParent] Not connected, reloading and re-establishing handshake before sendAction");
await this.reloadAndHandshake();
} else if (!this.isConnected) {
console.info("[WebViewParent] Handshake already in progress, waiting for it to complete");
await this.handshakeWithChild();
}
```
How can I resolve this? If you propose a fix, please make it concise.Co-Authored-By: Panayiotis Halios <panos@paella.dev>
| if (!this.isConnected) { | ||
| try { | ||
| console.info("[WebViewParent] Not connected, attempting handshake before sendAction"); | ||
| await this.handshakeWithChild(); |
There was a problem hiding this comment.
🚩 Unconditional reload on !isConnected regardless of recovery option
The new !isConnected check at line 105 triggers reloadAndHandshake() unconditionally, even when no recovery option is configured. In contrast, the error-code-based recovery path at line 112 is gated by this.recoveryOptions via isRecoverableError() (Parent.ts:85-86). The recovery option's JSDoc at Parent.ts:24-26 states: "When not provided, no automatic recovery is performed (backward compatible)." This documentation is now inaccurate since the !isConnected auto-recovery applies to all WebViewParent instances. This may be intentional (the changeset describes it as a general improvement), but the inconsistency with the documented contract is worth noting. Consider either gating the !isConnected recovery behind this.recoveryOptions != null, or updating the documentation to reflect the new unconditional behavior.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
closing in favour of https://github.com/Crossmint/crossmint-sdk/pull/1633/changes |
Description
When the React Native WebView handshake fails (timeout or error), subsequent
sendActioncalls now automatically reload the WebView and retry the handshake before sending the action. This makes the RN path self-healing, similar to how the web iframe path re-creates the iframe on failure.The change adds a single guard at the top of
WebViewParent.sendAction: ifthis.isConnectedis false, it calls the existingreloadAndHandshake()(which already uses a single-flight pattern) before proceeding.Human review checklist
isConnectedsemantics: Confirm there are no transient states whereisConnectedis false but a reload would be counterproductive (e.g., during initial load before the first handshake completes). Note thatonWebViewLoadin the provider setsparent.isConnected = falsebefore callinghandshakeWithChild(), and that call is not routed throughreloadAndHandshake(), so the single-flight pattern won't deduplicate an earlysendActionagainst the initial handshake.isConnectedis false and the subsequent action returns a recoverable error,reloadAndHandshake()would be called twice in sequence. Verify this is acceptable or unreachable in practice.sendActioncalls: Multiple calls arriving while disconnected will all awaitreloadAndHandshake(). The single-flight pattern should coalesce them, but worth confirming.Link to Devin run: https://crossmint.devinenterprise.com/sessions/2a0eb82cef7b442caaeac0ff1a9e2331
Requested by: @panosinthezone
Test plan
reloadAndHandshakesingle-flight logic. Manual testing on a React Native app where the initial handshake times out would confirm the auto-recovery path.Package updates
@crossmint/client-sdk-rn-window— patch changeset added (rn-webview-auto-recovery.md).