I recently went down the rabbit hole trying to fix an issue I was having with a seemingly simple react-navigation implementation of a TabNavigator. I followed the docs exactly and didn’t have anything out of the ordinary in my code (or so I thought). The issue was that every time I selected a new tab of the tab navigator, the screen would “jump” as it was rendering.
It looked like this:
Why is the screen jumping 😢
It would happen so fast that it didn’t seem like a big deal at first, but as the issue persisted it became an increasing frustration. I couldn’t find or think of anything that would cause this problem, and the GitHub issues page for react-navigation gave me no help either. Part of the issue was also that people would probably use a lot of different nomenclatures to describe the issue, whether it be “jank”, or “jumping” or “stuttering”. But the biggest issue was that I really didn’t understand the problem well enough to even compose a proper search for the solution…
After taking a QuickTime video of the screen jumping, and playing it in slow motion to see what exactly was happening, I noticed that the additional padding above the StatusBar was briefly disappearing during the jump. Interesting. This got me thinking about the usage of React Native’s SafeAreaView since that is what provides the additional padding on the top.
I looked at the official documentation, https://facebook.github.io/react-native/docs/safeareaview but there wasn’t anything interesting there. Searching the react-navigation documentation for “SafeAreaView” yielded this: https://reactnavigation.org/docs/1.x/handling-iphonex/.
👀 This section jumped out at me:
That sounded like exactly what I needed!
forceInset="top" But it also raised some additional questions… why is
forceInset recommended by react-navigation, but not in the official docs? Why was it jumping at all if I’m only trying to leverage the insets that should be the default for a SafeAreaView? 🤔
Boom! I stumbled on to the react-native-community page for SafeAreaView, which differs from the primary documentation: https://github.com/react-native-community/react-native-safe-area-view. The react-native-community SafeAreaView does expose the forceInset prop, and documents it as follows:
Sometimes you will observe unexpected behavior and jank because SafeAreaView uses onLayout…
YES. This is exactly what I was missing. The screen jumping was the “onLayout” call happening under the hood. The solution (as suggested by react-navigation) was to use
forceInset="top" to preload the top inset prior to the onLayout call. The other issue is that
forceInset is not available on the SafeAreaView exported directly from react-native, but only on the one in the react-native-community package.🕵
With these changes in place our app looks like it should:
In summary, when using SafeAreaView with the react-navigation TabNavigator, be sure to add the react-native-community SafeAreaView, and specify
forceInsert="top" on all of your tab screens.
I hope this helps some of my fellow react native developers!