This post is about how I got the animation work smooth on React Native for a given use case. I’ll first walk you through the possible ways to animate on React Native, then the challenges I faced and why I chose a particular approach.
Use Case: I need to show a number roller animation on the Home screen on every App Launch.
I didn’t know how to do this animation on ReactNative, when I googled, I came across this library and here is how I tried the sample app.
When I ran the above code, below is the animation I saw.
The animation is working as desired in the sample app. It’s time to evaluate if the animation works the same in a real-world production app.
Challenges in Real-world Production app:
While the user is landing on the HomeScreen JS thread is too busy executing the app’s business logic, processing network requests, creating UI objects to render.
If the Home screen is built with a Nested scroll view i.e multiple Horizontal Scrollviews inside a Vertical Scrollview with more than 2 vertical screen offset pages then imagine how jerky the animation would be like.
Here is what happened when I mocked the above challenges in the sample app by adding the below code.
So, keeping the JS thread busy made animation Jerky. This is not the experience I want to give to the users.
Is there a way to improve the above code?
I started exploring library code to see if there is a scope for improving the performance.
After reading the library’s code I knew it is not possible to improve animation performance for the following reason.
The math behind the animation of every frame is calculated on JS Thread. The calculated values are passed through the props node attached to a View. Then, the JS thread serialises the View object and passes it over the Native bridge to the Native thread. While the JS thread is busy executing business logic/processing network requests, it won’t be able to calculate a new value within 16.67ms. Hence we see the lag/frame drop.
After doing a bit more research I came across this blog which talks about using Native Driver.
Using Native Driver won’t improve the performance of animation in my UseCase for the below reasons
Native driver config works just, with in-build Animated library of react-native SDK. Also, You can only animate non-layout properties. Ex: transform and opacity will work but flex-box and position properties won’t.
React Native Re-Animated has Re-Designed and worked on new architecture whose primary goal is to get rid of the native bridge and directly communicate with Native Thread. With the help of Worklets, it executes part of JS code on a separate JS Virtual Machine which can run synchronously with Native UI thread without frame drops.
Cons — It’s still in the experimental phase, doesn’t have the full support and you may also notice intermittent crashes.
So, approaches #1, #2, #3 discussed above are not the options for me to try.
I want to trigger animation with required props from JS thread and pass it on to Native thread and let Native thread, maintain the context and do the math behind the animation for every frame.
By going through this blog I understood how to create Native modules and export them to React Native. Now, let’s create and export one.
Exporting Native component to ReactNative
Using Native component in React Native
Let’s see JS and Native thread animation at the same time in action by keeping JS thread busy.
It doesn’t matter how busy JS thread is, our NumberRolling animation is going to be performant at any circumstance/device configuration.
Note: You can implement the same on IOS too.