React Native is a great framework that lets you create cross platform mobile applications. It's especially helpful if you're a web developer and want a fast, low cost solution to develop native mobile applications that work on both Android and iOS. Bonus: you don't have to spend $$$ on separate iOS, Android, and web teams.
This not only decreases your costs, but also allows you to share a chunk of your codebase among all three (Android, iOS and Web) builds, making it easier to make changes and roll out updates.
React Native also allows you to work pretty close to the actual OS, which is important for performance critical tasks like animations.
Animations are an integral part of any application because they make it interactive and more appealing to the end user. But a lot of times, while working with animations, you might feel like you're working with a black box.
So let's learn how animations work in React Native. You can start learning animations in React Native from scratch from this free YouTube playlist.
The Animated API
React Native exposes an API called Animated. It consists of a lot of wonderful things like animatable values, spring/timing animations, and events. But we're not here to discuss the API – I'll leave that to the official docs and my YouTube playlist. They do a much better job covering that for you.
What we want to discuss here is, in fact, how React Native animates stuff on the screen and what goes on under the hood.
Two Ways to Animate
First of all, let's get one fact clear. React Native constructs actual native views on the screen, and not the ones rendered through embedded web browsers like Ionic. Because of this, if you want to animate a view in any way, that ultimately has to be done on the native view.
Do work in JS and update the native view
- The animation starts
requestAnimationFramefunction - a function which tries to run at 60 calls/second (60 FPS)
- At the other end of bridge, Java (Android) or Objective C (iOS) deserializes this value and applies the given transformations on the view mentioned.
- The frame is updated on the screen.
Did you see what happened there? None of the steps actually re-render a React Native component. This means that the Animated API actually "bypasses" React's philosophy of not mutating "state" variables.
The good news: this is actually helpful in case of animations because it'll be way too slow and bad for performance if we let React re-render a component 60 times a second!
- You can have very sophisticated animations written in JS and visible as native animations.
- More control over the animation state
- If the bridge is busy, there is decreased performance when the OS/JS want to communicate with each other.
Why do JS animations lag?
The animations done in JS will start lagging when the animation is going on and the user (or app) requests some other action which must be handled by the JS thread.
For example, imagine there's an animation occurring. This means that JS is busy running the
requestAnimationFrame function. Assuming that the updates are themselves not too heavy, suppose a user starts tapping a button on screen which increments a counter.
Now with the
requestAnimationFrame being called frequently, your React virtual tree is also re-rendering again and again in order to account for the increased counter.
Both of them are CPU bound tasks running on a single thread, so there will be a performance hit.
requestAnimationFrame will start dropping frames because of additional work being done by the JS thread.
All of this means you'll get a very jerky animation.
Native Driver Animations
Worry not, because React Native actually allows you to run animations on the native driver as well! What do I mean by that, you might ask?
Well, simply put, it means that React Native offloads the animation work from the JS thread to the UI thread (the OS) and lets it handle the animation of the object. This has a couple of benefits:
- The JS thread (and the React Native bridge) is now free for handling other intensive tasks like repetitive taps from user.
- Animations are much smoother because there is no serialization/deserialization and bridge communication overhead.
How does React Native accomplish that? The folks at React Native allow you as a developer to provide a property called
useNativeDriver as a boolean value when you're constructing an animation object.
When set to true, React Native, before starting the animation, serializes the whole animation state and what needs to be done in the future. It then transfers it once over the bridge to the OS.
This speeds up animations a lot and the animations run more smoothly, especially on low end devices. Let's see a video demonstration of Native Animations vs JS-based animations in React Native:
In this case, the steps roughly look like this:
- The animation starts
- JS serializes the animation info and sends it over the bridge.
- At the other end, OS receives that information and runs the animation natively.
That's it! Animations are much lighter for the JS thread now. No more
requestAnimationFrame running endlessly.
However, this method has its own share of pros and cons.
- Faster animations
- Non blocking JS thread
- Less bloated bridge
- Less control over animations (JS cannot see what's happening on the screen once the "automatic" animation starts)
- Less properties to manipulate - the native driver does not support all the properties to be animated. For example,
heightare not natively animatable, but
In a lot of cases, you'll find that you can work around a different set of animations using the
useNativeDriver: true to create a similar effect which cannot be achieved without setting
The React Native team is working on adding support for more properties as we speak, but for now, I believe it works just fine at the moment.
This article showed you how React Native animations actually work under the hood and why they are beautiful.
What do you think about this article? Tell me by connecting with me on my Instagram and Twitter accounts! New to React Native? Start learning it on codedamn - a platform for developers to learn and connect!