Skip to content

Commit 9d498f6

Browse files
rozelemeta-codesync[bot]
authored andcommitted
Defer focus until attached (#54309)
Summary: Pull Request resolved: #54309 We can encounter cases where something is rendered by React but not yet attached to Android at the point focus is called. If we defer focus until the View is attached to the window, we can avoid apps needing to add annoying timeouts or other creative mechanisms to wait for native mount before calling imperative focus. The only caveat here is that if you call imperative focus on something offscreen initially that is attached later (e.g., due to clipping), apps could encounter undesirable bugs where focus jumps at the point the previously detached view is now attached. One might also argue it's bug prone to call focus on something that is far offscreen / you don't plan to mount onscreen immediately. ## Changelog [Android][Fixed] Defers focus until View is attached Reviewed By: sbuggay Differential Revision: D85727424 fbshipit-source-id: 677834aa2d9ba2d3247d1e71b3fe0cdd9a6ec4aa
1 parent da5f15a commit 9d498f6

File tree

1 file changed

+20
-1
lines changed
  • packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view

1 file changed

+20
-1
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ public open class ReactViewGroup public constructor(context: Context?) :
153153
private var accessibilityStateChangeListener:
154154
AccessibilityManager.AccessibilityStateChangeListener? =
155155
null
156+
private var focusOnAttach = false
157+
private var hasAttachedToWindowForFocus = false
156158

157159
init {
158160
initView()
@@ -212,6 +214,10 @@ public open class ReactViewGroup public constructor(context: Context?) :
212214
updateBackgroundDrawable(null)
213215

214216
resetPointerEvents()
217+
218+
// In case a focus was attempted but the view never attached, reset to false
219+
focusOnAttach = false
220+
hasAttachedToWindowForFocus = false
215221
}
216222

217223
private var _drawingOrderHelper: ViewGroupDrawingOrderHelper? = null
@@ -417,10 +423,17 @@ public open class ReactViewGroup public constructor(context: Context?) :
417423
}
418424

419425
internal fun requestFocusFromJS() {
420-
super.requestFocus(FOCUS_DOWN, null)
426+
// We need a local variable here as opposed to the View.isAttachedToWindow check
427+
// since the value is not updated until after the Fabric commit.
428+
if (hasAttachedToWindowForFocus) {
429+
super.requestFocus(FOCUS_DOWN, null)
430+
} else {
431+
focusOnAttach = true
432+
}
421433
}
422434

423435
internal fun clearFocusFromJS() {
436+
focusOnAttach = false
424437
super.clearFocus()
425438
}
426439

@@ -572,6 +585,12 @@ public open class ReactViewGroup public constructor(context: Context?) :
572585
if (_removeClippedSubviews) {
573586
updateClippingRect()
574587
}
588+
589+
hasAttachedToWindowForFocus = true
590+
if (focusOnAttach) {
591+
requestFocusFromJS()
592+
focusOnAttach = false
593+
}
575594
}
576595

577596
private fun customDrawOrderDisabled(): Boolean {

0 commit comments

Comments
 (0)