Performance optimization in React Native

kahmun
AIA Singapore Technology Blog
6 min readMar 13, 2023

--

Waiting for an app to load is like waiting for a pot of water to boil. ⏳

When you are scrolling through an app, or visit a website, sometimes you may notice certain content seem to load more slowly than others. It’s natural to wonder if the issue is related to your phone or network. Then you start frustrating about it.

In today’s fast paced world, building responsive and high performant app is more important than before. Users expect seamless experiences with blazing-fast load time. However, as an application grows in complexity, we may be experience performance issues that can impact user experiences. Not only that, it also impacts usability and results in a loss of interest from audience.

Consequently, performance tuning has become an essential part of developer’s life, regardless of the specific technology or the framework being used.

One day, I was tasked with improving the performance of customer listing that needed to load from 500 up to 6000 datasets. Unfortunately, the initial load time was already quite slow, taking close to 15 seconds or more to load 500 datasets. 😤 This meant that any additional load time from a larger listing would be unacceptable to users, and even myself. Therefore, it is crucial to optimize the performance as much as possible to keep the load time at minimum.

In case of React Native, performance tuning can take many forms, from optimizing the app’s rendering to reducing the number of unnecessary re-renders. Let’s roll up our sleeves and get started to dive into our journey of performance optimizing.

Remove console.log statements 🧚‍♂️

Are you the one who always forgets to remove console.log statements? It has a negative impact on performance as well as slower code execution. To avoid this, it’s important to remove all console.log statements before deploying the app to production.

Best practices and efficient code 🧚‍♂️

Many of us might not be aware that certain coding practices can have a small impact on performance, especially if the component is re-rendered frequently. For example,

<Button
onPress={() => {
setError(true);
setLoading(false);
}}
>
</Button>

The arrow function creates every time the component is rendered, which can cause unnecessary re-renders, we can define the function outside and pass it as a reference, hence, the handlePress is only created once.

const handlePress = () => {
setError(true);
setLoading(false);
};

// Inside your component
<Button onPress={handlePress} />

Most of the time, following best practices can certainly improve performance, it keeps the code cleaner, more readable, maintainable in the future.

Make use of Functional Component 🧚‍♂️

It can be more difficult to tune performance with class-based component than with functional component due to the complex structure and reliance on lifecycle methods.

While functional component is simpler and more concise, there are still some situations where class-based component might be appropriate like validating complex state.

In this case, when it comes to fetching and rendering a huge list of data, functional component might be a better choice for performance reasons. The React hooks is optimized for performance and help prevent unnecessary re-renders or other computations.

Rewriting the component with functional component can be relatively straightforward process, as below:

Class-based component

import React, { Component } from 'react';
import { View, Text } from 'react-native';

class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}

render() {
return (
<View>
<Text>Hello World!</Text>
</View>
);
}
}

export default MyComponent;

Functional component

import React, { useState } from 'react';
import { View, Text } from 'react-native';

const MyComponent = () => {
const [count, setCount] = useState(0);

return (
<View>
<Text>Hello World!</Text>
</View>
);
};

export default MyComponent;

However, the decision to use class-based or functional component depends on the needs of an application or specific requirements. It is advisable to measure and compare the performance of both approaches before making the final decision.

React Query 🧚‍♂️

React query is known for one of the popular data fetching library that is optimized for server state caching, real-time data synchronization and background updates.

Before using React Query

const [list, setList] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);

useEffect(() => {
const fetchList = async () => {
setIsError(false);
setIsLoading(true);

try {
const result = await fetch('https://example.com/list');
const json = await result.json();
setData(json);
} catch (error) {
setIsError(true);
}

setIsLoading(false);
};

fetchList();
}, []);

One thing I like the most about React Query is that we don’t need to manage the loading state and error state ourselves using useState. Instead, we can use useQuery hook to fetch the data and handle the loading and error states automatically.

After using React-Query

import { useQuery } from 'react-query';

function MyComponent() {
const { isLoading, error, data } = useQuery('list', () =>
fetch('https://sample.com/list').then(res => res.json());

if (isLoading) return <View>'Loading...'</View>;
if (error) return <View>`An error has occurred: ${error.message}`</View>;

return (
<View>
<Text>{data}</Text>
</View>
);
}

The data fetching is done just in one line and thus it reduces amount of code and significantly improves the performance without having unnecessary re-renders caused by updating the state.

Some of you might wonder if Redux can be replaced by React Query? The answer is NO ❌. Both Redux and React Query are optimized for different use cases. Redux, on the other hand, provides a predictable global state management that can be used throughout an application.

In fact, both libraries can be used together to create a more robust application — a more efficient data fetching with React Query and a complete UI state management with Redux.

Remove unnecessary API calls 🧚‍♂️

Checking for unnecessary API calls is an essential part of performance optimization as well. In many cases, API calls can be expensive so make sure that they are only made when necessary.

It also can be achieved through caching and conditional rendering. This means that when the data is fetched, it is stored in a cache. When the data is requested again, it can be retrieved from the cache rather than making another API call.

Then again, if the data is only required for a specific component, it can only be requested and rendered when the component is active.

Partial data fetching 🧚‍♂️

Another feasible approach is to fetch a portion of data, e.g. the first 100 out of 500 datasets, followed by requesting and rendering the rest of the data.

Some modifications needed to be made on the API endpoint by specifying the number of data to be retrieved. The API endpoint can be modified to accept a query parameter limit that specifies the maximum number of data to be retrieved in a single request.

In client side code, the application makes an initial request to retrieve the first 100 data and render them on the screen. Then, make the request again from the background to fetch the entire data.

Partial data fetching also can be achieved through a several methods, including pagination, infinite scrolling, and lazy loading. Pagination allows dividing the data into smaller chunks and displaying them on separate pages, while infinite scrolling loads more data as the user scrolls down the page and lazy loading involves loading data as it becomes necessary, such as when the user clicks on a particular section.

As a result, it improves the performance and usability of the application, as well as reduce the server load and optimize bandwidth usage.

Voila! ✨ By implementing these approaches for performance optimization, we were able to drastically reduce customer listing’s load time from an average of 15 seconds to just only 2 seconds with the help from our back-end optmization as well. Our users become happy again. 🤪

But keep in mind, the application performance deteriorates when it grows in complexity, new features are added and more data is processed. Therefore, performance optimization is an ongoing process and be sure to continually adjust your app as needed. 💪🏻

--

--