TextInput autoFocus causing Navigation animation glitch


Recently at work, I encounter a weird animation glitch bug while navigate to a new page with keyboard focus. At first I thought it was cause by the logic of navigation, but later on it turn outs to be TextInput .


Actual Problem

With some digging, I find out it was cause by using TextInput 's autoFocus. And there's already a PR fixing it, thanks to @janicduplessis 's contribution. In summary, requestAnimationFrame causes the focus command to be sent in the same batch as starting screen transitions.

截圖 2020-03-08 下午12.54.11.png

More in @janicduplessis point of view

Looking at the iOS code I don't see how a race could happen since it calls UIManager.focus which then uses addUIBlock to focus the input. All the operation should happen in order so the new input will always be created and attached before focus is called. On android it uses dispatchViewManagerCommand which uses UIViewOperationQueue so it will also be properly synced with view creating operations.

Might have been a workaround for an old race condition bug that was since then fixed.

It seems like a bug with UIKit, It must assume keyboard focus will happen in the same event loop as the transition. Normally you'd call becomeFirstResponder in viewWillAppear. This will cause the keyboard to slide in from right to left smoothly with the new screen animation. When removing the raF it will cause pretty much the same thing to happen.

Just tested on Android and it causes the keyboard to open then close immediately :( This explains why this was added.

How to fix it?

This really depends on which RN version you're in. If you're usingv0.62.0-rc.0, then good news is it's already fixed.

But to upgrade to the latest RN version isn't that easy, so my suggestion is to do the autoFocus manually. By calling focus() manually in componentdidmount and set some delay with setTimeout() should do the trick.

Follow up

In the recent commit of RN, JS implementation on autoFocus has been removed. We no longer need to call focus on mount from JS as both iOS and Android implements it natively now.