Skip to content

Blur focused TextInput before unregistering it on unmount (fixes Android keyboard staying open)#57584

Open
sidorchukandrew wants to merge 3 commits into
react:mainfrom
sidorchukandrew:fix/textinput-blur-before-unregister
Open

Blur focused TextInput before unregistering it on unmount (fixes Android keyboard staying open)#57584
sidorchukandrew wants to merge 3 commits into
react:mainfrom
sidorchukandrew:fix/textinput-blur-before-unregister

Conversation

@sidorchukandrew

Copy link
Copy Markdown

Summary

Since #54085 ("Genalize focus/blur behavior in JS"), ReactNativeElement.blur() looks at TextInputState.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:

TextInputState.unregisterInput(inputRefValue);          // removes it from the set
if (TextInputState.currentlyFocusedInput() === inputRefValue) {
  nullthrows(inputRefValue).blur();                     // isTextInput() now false -> no-op
}

Since the input is unregistered first, blur() doesn't do anything: the native blur command is not dispatched. unregisterInput deletes 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 the focus() side, where registerInput() was moved into the ref callback so focus() 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:

  • New Fantom test in TextInput-itest.js: dispatches the blur command when a focused input is unmounted — asserts the blur command is dispatched and TextInput.State.currentlyFocusedInput() is cleared. Fails before this change, passes after.

I manually reproduced the bug and the bug fix with the following:

  • React Native at 86
  • @react-navigation/native-stack
  • react-native-screens
  • react-native-webview

The app has 2 screens: Screen A has a webview and Screen B has an input that is auto-focused.

  1. User navigates from Screen A -> Screen B
  2. The input is auto-focused. They keyboard opens
  3. The user presses the back button and navigates to Screen B
  4. The webview can still hold focus (according to Android OS) so the keyboard stays open

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
@meta-cla

meta-cla Bot commented Jul 16, 2026

Copy link
Copy Markdown

Hi @sidorchukandrew!

Thank you for your pull request and welcome to our community.

Action Required

In 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.

Process

In 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 CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@meta-cla

meta-cla Bot commented Jul 16, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 16, 2026
@meta-cla

meta-cla Bot commented Jul 16, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant