Abort on Blur or Block Removal: Choosing the Exit Policy for a Stalled Native-Stack Submit
0 reputation · 12 Nov 2025, 05:38 UTC
0 reputation · 12 Nov 2025, 05:38 UTC
A native-stack screen runs a submit that must survive a slow network but must not trap the user if the request never settles. React Navigation exposes two documented exits: cleanup in useFocusEffect to abort the request when the screen blurs, or e.preventDefault() in a beforeRemove listener to hold the stack action until the submit resolves. The library itself supplies no timeout, so any deadline is application code.
The trade-off is unresolved because the hooks answer different questions. Blur cleanup cancels side effects when visibility is lost, which also fires for tab switches or overlays where the screen is never removed. beforeRemove protects against swipe-back but does not fire on blur, and calling preventDefault() without re-dispatching the original action can leave the gesture handler inconsistent. Whether timers and promises keep running while inactive depends on freezeOnBlur, unmountOnBlur, and react-native-screens, with behavior that varies across installed versions.
Given that the navigator defines no wait-then-leave policy, which exit should own the timeout: force the original action and discard the submit, keep preventDefault and risk a dead back gesture, or surface a still-saving state? Should the policy differ when the screen is merely blurred versus actually removed?
35275 reputation · 12 Nov 2025, 06:07 UTC
The submit logic must own the deadline. On blur, use useFocusEffect cleanup to abort the in-flight request. On removal, use beforeRemove with e.preventDefault() to block briefly, then force-dispatch the original navigation action after the deadline. A dead back gesture is a worse user experience than a lost write.
useFocusEffect cleanup fires on blur (tab switch, overlay, or screen hide) and on unmount. This is the only reliable place to call AbortController.abort() and cancel the network request. The screen may remain mounted during a tab switch, so the request must be cancellable without blocking navigation.
beforeRemove fires only when the screen is about to be removed from the stack (back gesture or navigation.goBack()). Calling e.preventDefault() holds the gesture, preventing the user from leaving until the submit resolves or the timeout fires. If the deadline passes while blocked, call e.dispatch(e.data.action) to force the original navigation and unblock the gesture handler.
useFocusEffect, register cleanup that aborts the controller on blur.beforeRemove, prevent default, start a monotonic deadline timer, and resolve/reject the submit.e.dispatch(e.data.action) to force navigation and discard the request.e.preventDefault(false) to release the gesture.freezeOnBlur and unmountOnBlur control whether timers and promise continuations keep running while the screen is inactive. react-native-screens may freeze JavaScript execution in blurred screens depending on version. Use a monotonic timestamp (e.g., performance.now() or Date.now()) to check elapsed time rather than relying on setTimeout callbacks that may not fire.
useFocusEffect cleanup aborts the fetch when switching tabs or opening overlays.beforeRemove blocks the back gesture and allows escape after the deadline.Blur: Abort side effects, keep the screen mounted if desired. The user is still in context; a cancelled request is acceptable.
Removal: Block briefly for a graceful save, then force-leave after the deadline. Losing a write is preferable to trapping the user.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.