Performance Tips for React Native Apps
Tags: reactnative, mobile, performance
Published: 2023-01-12 01:08:35

by [Joakim Gakure](https://blog.openreplay.com/authors/joakim-gakure)


Performance is a critical consideration when developing web pages and applications. A fast, responsive application can provide a better user experience, increasing user satisfaction and engagement. On the other hand, slow loading times or unresponsive behavior can frustrate users and lead to decreased usage or abandonment. Therefore, it is essential to carefully consider performance when building webpages and applications to ensure a smooth, seamless user experience.

As a developer, your [React Native](https://reactnative.dev/) needs to be fast and fully optimized, depending on how you write your code. This article will cover techniques and tips to optimize React Native apps for higher performance. Buckle up and get ready to boost them like a pro

## Reasons for a Slow React Native App

A React Native app may be underperforming for several reasons. The following are some typical reasons why React Native apps operate slowly:

- Complex UI. An app with a complex UI with many nested components takes longer to render, leading to poor performance.
- Unnecessary re-renders cause the app to slow down. This can be caused by excessively using the `setState` method or not using the `shouldComponentUpdate` lifecycle method. Components that re-render unnecessarily or perform expensive calculations on each render cause application performance overheads.
- Large component hierarchies: Poorly designed component architecture that has a large number of children, it takes longer for the component to render.
- A large amount of data takes longer to render, leading to poor performance, especially if the component renders a large dataset inefficiently.
- Complex animations. Animations that are implemented poorly or that are too complex slow down component rendering.
- Memory leaks occur when an app uses more and more memory over time, even when the application does not need an operation at a given time. Memory leaks keep the operation holding memory, and the application expects such operation to release the assigned memory and assign it to the subsequent operations. Lack of such memory reallocation leads to memory leakage.
- Complex calculations performing complex calculations takes longer for the component to render.
- Lack of optimization. Your components need to be optimized for performance, and the lack of such practices is caused by using the wrong components or not using performance optimization techniques such as memoization.

Identifying such root causes of the slow performance is the first step to jumping in and finding the best solutions to address the elephant in the room. The following sections will discuss the common things that you need to do and improve the performance of your React Native app.

## CSS Animations Instead of JS Animations

Animations play a significant role in enhancing the user experience of an app. There are various ways to implement animations in React Native applications, including CSS and [JavaScript-based animations](https://javascript.info/js-animation). Choosing the proper animation technique helps create a performant and more enjoyable user experience for the app.

JavaScript-based animations rely on JavaScript runtime. Using [CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations) instead of JavaScript-based animations potentially improves the performance of a React Native app. This is because the browser's rendering engine handles CSS animations. This engine is optimized for performance out of the box for animation performance and frame rate.

CSS animations allow you to specify a series of keyframes. These are points in the animation process that define the appearance of an element. Each [keyframe uses CSS](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes) to specify the element's appearance at a specific animation stage. The browser then creates the animation by transitioning from one keyframe to the next. Check out this basic component example that uses CSS animations:

```javascript
import React, { useRef } from 'react';
import { View, Text, StyleSheet } from 'react-native';

function EXComponent() {
  const elementRef = useRef(null);

 function startAnimation() {   
   elementRef.current.classList.add('animation');
  }

  return (
    <View style={styles.container}>
      <Text ref={elementRef} style={styles.text}>Hello World</Text>
      <Button title="Start Animation" onPress={startAnimation} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justify-content: 'center',
  },
  text: {
    fontSize: 20,
  },

  'animation': {
    animation: 'fadeIn 1s',
  },
  '@keyframes fadeIn': {
    from: {
      opacity: 0,
    },
    to: {
      opacity: 1,
    },
  },
});

export default EXComponent;
```

## FlatList Instead ScrollView

Rendering large data sets is part of application development. However, the approach you choose to load data to your application determines the overall performance of a given component. In React Native, you display a data list in two significant ways: `FlatList` and `ScrollView`.

Each approach has its underlying performance to the component used for rendering these data sets.

The [FlatList](https://reactnative.dev/docs/flatlist) uses a 'virtualized' rendering technique. This means that it only renders the currently visible items on the screen. It reduces the number of elements that need to be rendered and managed by the app based on the user's current screen in relation to the position of an item in that data list.

```javascript
import React from 'react';
import { FlatList, Text, View } from 'react-native';

const data = [
  { id: '1', title: 'Test Item', description: 'This is the description'},
  { id: '2', title: 'Test Item', description: 'This is the description'},
  { id: '3', title: 'Test Item', description: 'This is the description'},
];

function MyList() {
  return (
    <FlatList
      data={data}
      renderItem={({ item }) => (
        <View>
          <Text>{item.title}</Text>
          <Text>{item.description}</Text>
        </View>
      )}
      keyExtractor={item => item.id}
    />
  );
}

export default MyList;
```

Here `keyExtractor` prop is used to specify a unique key for each item in the list that will be displayed on the current user screen.

[ScrollView](https://reactnative.dev/docs/scrollview) is still desirable for rendering lists of data. However, it works fine for small lists of items. It renders all the items in the list, which is less efficient when rendering large lists.

```javascript
import React from 'react';
import { ScrollView, Text, View } from 'react-native';

function MyList({ data }) {
  return (
    <View>
      <ScrollView>
         /* rendering a small list of items */
        {data.slice(0, 10).map(item => (
          <View key={item.id}>
            <Text>{item.title}</Text>
           
<Text>{item.description}</Text>
          </View>
        ))}
      </ScrollView>
    </View>
  );
}

export default MyList;
```

`FlatList` is suited when rendering long lists of data. It only renders the items currently visible on the screen and removes items from the rendering tree when they are no longer visible to minimize memory usage. As a result, using a `FlatList` component makes your React Native app faster and more efficient.

Other techniques that come in handle include using [infinite scrolling](https://blog.openreplay.com/implementing-infinite-scrolling-in-react/) and implementing paginations to your lists of data.

## Memoize React Components

[Memoization](https://www.freecodecamp.org/news/memoization-in-javascript-and-react/) optimizes the performance of a function by storing the results of expensive function calls and returning the stored results when the same inputs occur again. It helps you avoid recalculating the results each time the function is called.

In the context of React Native, memoization is used to optimize the performance of components by avoiding unnecessary re-renders. When a component is memoized, its output is cached, and the component will only re-render if its inputs (props) change. This stores and reuses expensive rendering components, such as complex calculations and [DOM manipulation](https://reactnative.dev/docs/direct-manipulation).

React Native uses `React.memo()` to memoize a component. Below is a basic example of a memoized React Native component using [React.memo()](https://beta.reactjs.org/apis/react/memo) that will memoize and only re-render if the `Text` props changes:

```javascript
import React from 'react';
function TestComponent(props) {
  
  return <Text>{props.text}</Text>;
}
export default React.memo(TestComponent);
```

`React.memo()` is a higher-order component similar to `useMemo`. It is used for function components instead of hook functions. You can use the [useMemo](https://beta.reactjs.org/apis/react/useMemo) and [useCallback](https://beta.reactjs.org/apis/react/useCallback) hooks to memoize expensive calculations and functions. Here is a basic example:

```javascript
import { useMemo, useCallback } from 'react';
function TestComponent(props) {

  // useMemo
  const expensiveValue = useMemo(() => {
  }, [props.a, props.b]);

  // useCallback
  const expensiveFunction = useCallback(() => {
  }, [props.a, props.b]);

  return (
    <div>
      {expensiveValue}
      <button onClick={expensiveFunction}>Click me</button>
    </div>
  );
}
```

Memoization can increase the complexity of your code. Use memoization when it is necessary and beneficial for your specific use case.

<h2>Open Source Session Replay</h2> 

_<a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a> is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![OpenReplay](https://blog.openreplay.com/banner-blog_1oYPGT.png)

_Start enjoying your debugging experience - <a href="https://github.com/openreplay/openreplay" >start using OpenReplay for free</a>._



## Use Hermes

[Hermes](https://reactnative.dev/docs/hermes) is a JavaScript engine developed by Facebook. It is specifically designed to improve the performance of React Native applications on Android devices. However, it doesn't run by default. To enable it in your React Native project, follow these steps: Make sure you use React Native version 0.62 or higher.

- In your `android/app/build.gradle` file, add the following line to the `defaultConfig` block:

```java
defaultConfig {
ndk.abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
```

- In the `android/app/src/main/AndroidManifest.xml` file, add the following to the `application` element:

```xml
<application android:usesCleartextTraffic="true">
</application>
```

- Navigate to `android/app/src/main/java/com/[your-app-name]/MainApplication.java` file, and add the following import:

```java
import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
```

- In the same file, add the following lines to the getPackages method:

```java
@Override
protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
        new MainReactPackage(),
        new MyCustomReactPackage(),
        new HermesPackage()
    );
}
```

- In the same file, add the following methods:

```java
@Override
protected String getJSMainModuleName() {
    return "path/to/your/index";
}

@Override
protected ReactExecutorFactory getReactExecutorFactory() {
  return new HermesExecutorFactory();
}
```

Run `react-native run-android` to build and run your app. After enabling Hermes, you will notice improved performance in your apps at start-up time on Android devices.

## Avoid Props in Initial States

Props are (or should be!) immutable. Changing them causes unexpected behavior. Avoiding props as [initial state](https://reactnative.dev/docs/state.html) values help to ensure that your component's internal state is self-contained and easier to understand and debug.

Instead of using props, use the `useState` hook to define an initial state value for a component, for example:

```javascript
import { useState } from 'react';

function MyComponent(props) {
  const [value, setValue] = useState(props.initialValue);

  return (
    <div>
      <button onClick={() => setValue(value + 1)}>Increment</button>
      <p>{value}</p>
    </div>
  );
}
```

The `useState` hook defines an initial state value for the `value` variable. The value is initialized to the `initialValue` prop. This way, it can be changed by calling the `setValue` function. This allows the component to maintain its internal state, separate from the props it receives.

Alternatively, use `constructor` methods to achieve the same as follows:

```javascript
class TestComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: props.initialValue,
    };
  }

  render() {
    return (
      <div>
        <button onClick={() => this.setState({ value: this.state.value + 1 })}>
          Increment
        </button>
        <p>{this.state.value}</p>
      </div>
    );
  }
}
```

Here you avoid using the initial state value using a `constructor` method. It defines an initial state value for the `value` variable. The value is initialized to the `initialValue` prop, which is changed by calling the `setState` function.

## Escape Re-renders

Re-rendering is necessary at some points. However, there are scenarios in which re-renders are unnecessary. Let's check out a few ways to avoid re-renders. They include (With some strategies we have already discussed here):

- Use the `React.memo` higher-order component or the `PureComponent` base class to optimize the performance of the function and class components. These components will only re-render if their props have changed.
- Use the `useMemo` hook to memoize expensive computations and avoid re-computing them on every render.
- Use the `useCallback` hook to memoize callback functions and avoid creating `unnecessary` functions on every render.
- Always avoid using props or state values in the `render` method that are not used to determine the content of the component.
- Avoid using the `forceUpdate` method to trigger a re-render.

## Dependency and Code Optimization

In React, it is essential to carefully consider the dependencies that you use to perform different operations. React Native uses NPM dependencies. To your application bundle size, you need to consider the amount of code each package you install adds to your application in relation to the code you are utilizing and their dependencies.

For example, consider using [Moment.js](https://momentjs.com/). Use the `moment-locales-webpack-plugin` to remove unnecessary language support from your Moment.js library. This help to reduce the size of your final bundle. It is handy if you only use a single language in your app and don't need the additional localization files that come with Moment.js. To use the plugin, you will need to install it and add it to your [webpack](https://webpack.js.org/) configuration.

```javascript
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');

module.exports = {
  plugins: [
    // Other plugins here
    new MomentLocalesPlugin({
      localesToKeep: ['en'], 
    }),
  ],
};
```

Alternatively, use the `lodash-webpack-plugin` to remove unnecessary functions from your [Lodash](https://lodash.com/) library and reduce the size of your final bundle. This is especially useful if you only use a small subset of the functions provided by Lodash and don't need the rest of the library in your final bundle. To use the plugin, you will need to install it and add it to your webpack configuration as follows:

```javascript
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');

module.exports = {
  plugins: [
    new LodashModuleReplacementPlugin({
      collections: true,
      shorthands: true,
      paths: true,
    }),
  ],
};
```

When using console statements for debugging codes in a development phase, remove them before rolling out production versions. Use [babel-plugin-transform-remove-console](https://babeljs.io/docs/en/babel-plugin-transform-remove-console/) to remove these statements. Always remove unnecessary libraries and code, such as [unused CSS code, and improve performance with CSS](https://blog.openreplay.com/30-ways-to-improve-website-performance-with-css/).

These approaches help reduce your bundle size and improve your app's performance.

## Cache and Optimize Assets

In a React Native app, optimizing the size and performance of assets like images, fonts, and other media files is important. There are a few strategies you can use to optimize these assets:

- Image optimization tools such as `imagemin` to reduce the file size of images without sacrificing quality.
- Using vector graphics instead of raster graphics. Vector graphics can be scaled to any size without losing quality.
- Use font icons instead of image icons. Font icons are scalable and have a smaller file size.
- Add the `@2x` and `@3x` suffixes to specify different versions of an image for different screen densities, and use the `Image` component's `resizeMode` prop to control how the image is scaled.
- Use the `Image` component' source prop to specify a low-resolution version of an image as a placeholder while the high-resolution version is being loaded.
- Use the `ImageBackground` component to apply a tiling image as a background rather than a full-size image.
- If necessary, use SVG to create your application assets.

## Using a CDN

CDNs (content delivery networks) are distributed server systems that deliver content based on the user's location. An app served by a CDN redirects requests to the server closest to the user when a user requests content. This improves content delivery performance and speed.

CDNs are commonly used to deliver static content, such as images, fonts, and other media files. It ensures that your static assets are delivered to users as quickly as possible, regardless of location. The benefit of this is that it reduces the distance assets need to travel between the server and the client. This is useful for websites and apps with global audiences.

Your assets must be hosted on a CDN server and referenced in your React Native app's code using the CDN URL.

```javascript
<Image
  source={{
    uri: 'https://my-cdn.com/images/logo.png',
  }}
/>
```

Top CDN providers you can use include [Amazon CloudFront](https://www.amazonaws.cn/en/cloudfront/), [Cloudflare](https://www.cloudflare.com/learning/cdn/performance/), [Akamai](https://www.akamai.com/solutions/content-delivery-network), [MaxCDN](https://www.stackpath.com/maxcdn/), [Google Cloud CDN](https://cloud.google.com/cdn) etc.

## Conclusion

Optimization is an inevitable day-to-day practice for developers. Users require an application that serves them what they need excellently. As a developer, this places a demand you always need to accomplish while putting your users on the upper hand.





[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)