Blur focused TextInput before unregistering it on unmount (fixes Android keyboard staying open)#57584
Conversation
Since react#54085, ReactNativeElement.blur() routes through TextInputState.isTextInput(), which checks the registered-inputs set. TextInput's unmount cleanup unregistered the input before calling blur(), so the blur silently became a no-op: the native blur command was never dispatched, hideSoftKeyboard() never ran, and on Android the soft keyboard stayed open after unmounting a focused input (e.g. when popping a native-stack screen), with focus escaping to any remaining focusable view such as a WebView. Reordering the cleanup so blur() runs while the input is still registered restores the pre-0.83 behavior. This mirrors the fix that react#54085 itself applied on the focus() side, where registerInput() was moved earlier so focus() could be called from ref callbacks. Changelog: [GENERAL] [FIXED] - TextInput: blur focused input before unregistering it on unmount, fixing the Android soft keyboard staying open after navigating away from a focused input
|
Hi @sidorchukandrew! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
Summary
Since #54085 ("Genalize focus/blur behavior in JS"),
ReactNativeElement.blur()looks atTextInputState.isTextInput(this)to check if the text input is registered in the set before attempting to send the native command to blur.TextInput's unmount cleanup runs in this order:Since the input is unregistered first,
blur()doesn't do anything: the native blur command is not dispatched.unregisterInputdeletes the text input id from the set.On Android the visible symptom is that the soft keyboard stays open after unmounting a focused TextInput. Navigating back from a native-stack screen with a focused input will try to focus whatever focusable view remains in the window (a WebView, an SDK overlay, etc.), which then "owns" the stuck keyboard. Apps with no other touch-mode-focusable views don't see it (focus falls to null and Android hides the keyboard itself), which is likely why it went unnoticed.
This PR reorders the cleanup so
blur()runs while the input is still registered, restoring the pre-0.83 behavior. This mirrors the fix #54085 itself applied on thefocus()side, whereregisterInput()was moved into the ref callback sofocus()could be called before the layout effect registers the input.Bisect: 0.79.7 / 0.81.6 / 0.82.1 unaffected -> 0.83.0 broken -> reproduced through 0.86.0.
Changelog:
[GENERAL] [FIXED] - TextInput: blur focused input before unregistering it on unmount, fixing the Android soft keyboard staying open after navigating away from a focused input
Test Plan:
TextInput-itest.js:dispatches the blur command when a focused input is unmounted— asserts theblurcommand is dispatched andTextInput.State.currentlyFocusedInput()is cleared. Fails before this change, passes after.I manually reproduced the bug and the bug fix with the following:
@react-navigation/native-stackreact-native-screensreact-native-webviewThe app has 2 screens: Screen A has a webview and Screen B has an input that is auto-focused.