React Native Performance Roadmap
As Developers, we want to build apps that customers will love and spread with the world. To achieve that, a significant part of the app development community often chooses to develop their apps using React Native because they can deliver beautiful, high-quality, and multi-platform mobile apps.
React Native naturally offers a lot of optimization for designing high-performing applications out of the box. However, frequently you might face performance issues mostly related to your codebase structure. Slow apps or crashes will give your users a poor experience, and they will eventually uninstall it.
When talking about performance issues, people usually jump to the tips and trick optimization part, but we need to be aware of the tradeoff in terms of budget, time, effort, etc. The purpose of this article is to give you a clearer idea of what is essential to have in mind before starting a React Native project and to guide you on how to identify improvement opportunities for your App. We are not to encourage you to pick what is hip and trendy.
It would help if you grew some understanding of:
- Why is performance important?
- When is the right moment to seek optimizations?
- How to find solutions for your performance issues?
Contents
Application performance Impact
So, we want our users to love and share our apps, right? But we need a way to know if we are on the right path.
The churn rate is one of the metrics that most stand out in a mobile marketing job since it is the one that points to the loss of customers, that is, how many users abandoned your application. Another way to measure user satisfaction with our products is the conversion rate.
A user converts when they take the action that we wanted them to take. For example, on an e-commerce page displaying baby strollers, a user converts when they decide to purchase a stroller and click on the ‘buy’ button. Filling out and submitting a contact form, answering survey questions might also be considered a conversion.
Stability, performance, and resource usage impacts the churn and conversion rate directly. In other words, they improve the overall UX, which increases the ROI of your project or your product metrics.
When to optimize
If Anything takes longer than 100ms, the user will notice.
A study led by Dr. Gitte Lindgaard showed that people could make difficult decisions about a web page’s appearance shortly after being exposed to it for just 50ms. Other eye movement monitoring studies also show that the human eye moves very fast when browsing web pages or different stimuli without fixing on a point for more than 100ms.
We can assume that whenever our UI operations take longer than 100ms, we may have a performance issue that needs the team’s attention.
Keep in mind that this number is related to UI operations such as component rendering and animations. That is one reason the React Native core time suggests that animations should always run at 60 Frames per Second(FPS).
How to find solutions for your performance issues
To know how and where to find the right information is the software engineer’s second language. So, at this point, I present you with a React Native performance problem-solving dictionary. Also, I like to think about this as a high-level way to think about how to solve RN issues.
There are mainly two paths to take to reach the React Native performance land:
- Improving performance by understanding React Native implementation details and knowing how to make maximum out of it.
2. Improving performance by using the latest React Native features or turning on some of them.
Optimize by Understanding React Native
Pay attention to IU re-renders
Sometimes to optimize performance, you need to twerk some parts of your based on how component rendering affects your App. The nature of React Native is declarative; that is, you declare the components and say how they should behave, then React Native at the javascript thread takes care of the native API calls to the device.
A re-render can only be triggered if a component’s state has changed. The state can change from a props
change, or from a direct setState
change. Also, if a parent component re-renders all its children will also re-render.
For example, let’s imagine the following component:
Now suppose at some point in our application the power
attribute receives a new value. Then, React will run the render
method again, But wait…the component render method depends only on the name
attribute.
Possible solutions are:
- Using
shouldComponentUpdate
to decide whether the component should re-render or not
This method gets executed in React’s lifecycle and always returns a boolean value (true/false). In that case, when the power
attribute gets updated the shouldComponentUpdate
returns false
which prevents the component to re-render.
- Refactoring the implementation into using
PureComponent
Pure Components prevents re-rendering of component if props or state is the same. Therefore, restricts the re-rendering ensuring higher performance of the Component.
- Refactoring the implementation into using functional component and
React.memo
In this approach, React uses memoization which is a technique used to primarily speed up programs by storing the results of expensive function calls and returning the cached results when the same inputs occur again. In other words, as long as the name
prop remain the same our component will not re-render.
Use High-Order Components
React Native is a component world, and therefore everything is built on top of those building blocks. To compose our application, we use React Native primitive components such as Text, TextInput, View, etc. There is also another set called higher-order components that are designed and optimized to serve a particular purpose.
The fastest and code smelled way to create a list of elements would be to map through items using ScrollView and View primitive components. This approach can potentially affect your application performance, especially as real production data grows. FlatList is a High-order component for dealing with large data-sets, infinite scrolling, and memory management.
Before:
After:
Be more selective with external libraries
Library’s size affects the TTI (Time to interactive) of your App.
As you know, React Native apps contain a JavaScript bundle that needs to be loaded into memory and then parsed and executed by the JavaScript VM. While the loading process is happening, the application remains in the loading state. So by logic, less code means faster opening time!
According to Akamai’s report on online retail performance, just a one-second delay in mobile load times can cut the conversion rates by 20%. Try not to ignore being more library selective because it may seem irrelevant at first, but it can cost you many headaches later.
An essential piece of advice here is to search for the library over the internet, verify if healthy and well maintained by looking at the #Github stars, the number of issues, contributors, and PRs. Also, check the library’s size, number of supported features, and external dependencies.
A typical scenario is importing a complex library to use one feature or two. In this case, check if you can import only the parts you need or use a smaller library. For example, let’s say you need to manipulate dates. Instead of using moment.js ~(67.9kb)
you could use day.js ~(2kb)
Remember to use dedicated mobile libraries
It may seem obvious, but not every library is optimized for mobile. Using non-optimized libraries may cause some serious performance and user experience issues. For example, using a web version of a javascript library for mobile could affect overall CPU usage and battery drain.
Always run animations at 60FPS
You should already know this, but I will say it out loud: Animations are just static images displayed over time. Those images are called frames, and the number of frames showed each second directly impacts how smooth and interactive your App looks.
For most scenarios, React Native gives you the perfect 60fps frame rate. But in some cases, you might need to optimize animations manually.
Animations currently work using Animated with the JS driver, which means that animated values are serialized and sent over the bridge. The problem is that your app logic and other JS code also uses the same bridge and the amount of data you can pass into that channel is limited.
Just adding useNativeDriver: true
to the animation, config is the easiest way of improving the performance of your animations. That allows the animation to be sent only once over the bridge, and the native IU Thread will take care of frame rendering. For complex animations, Gesture Handler combined with Reanimated should give you excellent and smooth animations.
Before:
After:
For complex animations, Gesture Handler combined with Reanimated should give you awesome and smooth animations.
Optimize by using React Native latest features
Some co-workers have this theory that you should remain at least two releases behind to keep your App stable and away from new bugs that may appear on more recent releases.
But keeping your application up and running with the recent framework releases also gives you the latest features, performance improvements, and security fixes. If your application runs on an older React Native, you won’t use those goodies.
By staying behind wouldn’t be able to:
- Debug Faster and Better with Flipper
- Automate dependency management with auto-linking
- Optimize application startup time with Hermes
- Reduce Android application size with proguard Gradle settings
And the list goes on and on. It is up to you to decide but React Native is growing fast every day with new features, critical bug fixes, and security patches. On average, each release includes around 500 commits. So, by choosing to stay behind, you might get to a point where it will be almost impossible to upgrade your App along with its dependencies.
Conclusion
There is a lot of techniques that you can use in order to optimize some performance bottlenecks in our apps. This article showed the importance of thinking about app performance and presented a roadmap with the main topics on React Native performance optimization.
References:
https://github.com/kamranahmedse/developer-roadmap
https://reactnative.dev/docs/performance
https://www.nngroup.com/topic/eyetracking/
https://blog.bitsrc.io/optimize-your-react-app-with-react-memo-ec52447b09ba
https://www.nngroup.com/articles/powers-of-10-time-scales-in-ux/