Quick Summary:

The performance of mobile applications is a crucial factor, and speed plays a significant role in the success of an application. Let’s deep dive into discussing the approaches and strategies you require to speed up React Native app performance. This blog will guide you through the ways to improve your React Native app performance in 2024. (Learn about the new & amazing Hermes and other best practices to improve React Native app performance.)

Table of Contents

Introduction

React Native has been growing by leaps and bounds since its inception in 2015, and it is believed that this cross-platform app development framework has overtaken the likes of Xamarin and Iconic.

React Native uses several ingenious approaches for DoM transactions, saving the time to refresh the UI. The decision-makers understand the criticality of having a dedicated React Native team in building a React Native app architecture. However, certain bottlenecks occur while scaling up the functionalities and operations, such as memory leaks which hamper React Native app performance.

Let’s get straight to analyzing the 9 best practices for React Native performance optimization.

9 Ways to Improve React Native App Performance

To improve your React Native application performance, here are some common practices to follow, which will significantly benefit you in the long term.

1. Use Hermes

To get the best performance with Hermes and React Native, there are a few things you can do:

  • Enable Hermes in your React Native project: By default, React Native uses the JavaScriptCore engine to run JavaScript code. To use Hermes instead, you need to enable it in your project’s configuration file. Once enabled, Hermes will be used to compile and execute your JavaScript code, which can result in faster startup times and better runtime performance.
  • Use the most recent version of React Native: Because React Native has been optimized to work well with Hermes, utilizing the most recent version of React Native can help you get the most out of Hermes.
  • Optimize your code: While Hermes can improve the performance of your JavaScript code, it’s still important to write efficient code. This entails eliminating needless computations and reducing the number of times the DOM is accessed.
  • Test your app: Once you’ve enabled Hermes and optimized your code, be sure to test your app thoroughly to ensure that it’s performing as expected. Use profiling tools to identify any performance bottlenecks and address them accordingly.

2. Avoid Anonymous Functions while Rendering

In React Native, it is generally a good practice to avoid using anonymous functions when rendering components. The reason for this is that anonymous functions can cause unnecessary re-renders, which can negatively impact the performance of your app.

When you define an anonymous function inside the render method of a component, it creates a new function object every time the component renders. This means that even if the function has the same behavior and output, it will be treated as a new function and will cause a re-render.

To circumvent this problem, write your functions outside of the render method and provide them to your components as props. As a result, the function object will only be generated once, and your component will not excessively re-render.

Copy Text
import React from 'react';
import { TouchableOpacity, Text } from 'react-native';

const MyComponent = () => {
  const handleClick = () => {
    // Handle click event here
  }

  return (
    < TouchableOpacity onPress={handleClick} >
      <Text >Click me</ Text>
    </TouchableOpacity >
  );
}

export default MyComponent;

In the example above, we define the handleClick function outside of the render method and pass it as the onPress prop to the TouchableOpacity component. As a result, there will be no extra renderings and the function object will only be produced once.

React Native app’s speed can be enhanced by eliminating anonymous methods while rendering components.

3. Optimizing React-Native Memory Usage

React Native has a number of memory optimisation techniques that you may use to improve your app’s speed and minimize its memory footprint. Here are a couple such examples:
Virtualized Lists: React Native provides a FlatList component that virtualizes long lists, meaning that it only renders the items that are currently visible on the screen. This can significantly reduce the memory usage of your app, especially if you are working with large lists of data. Here’s an example of how to use the FlatList component:

Copy Text
import React from 'react';
import { FlatList, Text } from 'react-native';

const MyComponent = () => {
  const data = [{ key: 'item1' }, { key: 'item2' }, { key: 'item3' }];

  const renderItem = ({ item }) => (
    {item.key}
  );

  return (
    
  );
}
export default MyComponent;

Image Caching: React Native provides a built-in image caching mechanism that can help reduce the amount of memory used by your app. React Native caches images for the period of the app session by default. However, you may modify the caching behavior to better fit the demands of your programme. Here’s an example:

Copy Text
import React from 'react';
import { Image } from 'react-native';

const MyComponent = () => {
  const imageUrl = 'https://example.com/image.jpg';

  return (
    
  );
}

export default MyComponent;

Want to build a React Native app that breaks down barriers and connects with all users?
Hire React Native developer who has a deep understanding of accessibility principles and can implement them flawlessly.

4. Caching for Efficiency: Memoization of Costly Components

Memoization is a technique in React Native, and more generally in computer programming, that involves caching the results of a function call based on its input arguments, so that if the function is called again with the same arguments, the cached result can be returned instead of recalculating the result.

Memoization can be used to make your React Native app faster, especially if you have a lot of data flowing through it.

Performance boost: Memoization is a memory optimization technique that can be used to improve the performance of your application. The idea behind it is to store the results of expensive computations in an object and then reuse them whenever you need them again. This way, React Native doesn’t have to recalculate these values every time they’re needed.

Code readability: Memoization makes your code more readable because it reduces complexity by removing conditional statements from your functions’ bodies. For example, let’s say you have this function

Copy Text
import React, { useMemo } from 'react';

const ExpensiveComponent = ({ data }) => {
  const memoizedData = useMemo(() => {
    // Perform expensive calculation on data
    return data.map(item => item.name);
  }, [data]);

  // Use the memoized data in component rendering
  return (
    
      {memoizedData.join(', ')}
    
  );
};

export default ExpensiveComponent;

đźź  Memoization in React Native
React Native Memoization is a technique for storing the results of expensive computations, so that you can reuse them in future calls. There are two strategies for memoization in React Native:
â–ş Pure Components
â–ş Memoization with useMemo Hook

To implement memoization, you need to:

  • Define a function that accepts a callback (the function that will be called when the value is requested). This function should produce an object with two properties: ‘cache’ and ‘key’. The `cache` property contains the cached value if it exists, or null otherwise. The `key` property contains a unique identifier for this particular invocation of your memoized function.
  • Pass this new function as an argument to your original function call. This causes each call to be wrapped in your new wrapper function before being passed along to its original implementation.

a. Memoization with Recompose

Copy Text
import React from 'react';
import { View, Text } from 'react-native';
import { withMemo } from 'recompose';

const MyComponent = ({ name }) => {
  console.log('Rendering MyComponent...');

  return (
    < View >
      < Text >Hello, {name}!
    
  );
};
const MemoizedMyComponent = withMemo(
  ({ name }) => name, // Memoization key
  (props, nextProps) => props.name === nextProps.name, // Equality check
)(MyComponent);
export default MemoizedMyComponent;

b. Memoization with Reselect

Copy Text
import React from 'react';
import { View, Text } from 'react-native';
import { createSelector } from 'reselect';
const MyComponent = ({ name, age }) => {
  const greetingSelector = createSelector(
    name => name,
    age => age,
    (name, age) => `Hello, ${name}! You are ${age} years old.`
  );
  const greeting = greetingSelector(name, age);
  console.log('Rendering MyComponent...');
  return (
    < View >
      {greeting}
    
  );
};
export default MyComponent;

c. Memoization with Redux
In Redux, you can use memoization to optimize the performance of selectors, which are functions that compute derived data from the Redux store.

Here’s an example of how to use memoization with Redux selectors:

Copy Text
import { createSelector } from 'reselect';

// Define a selector that computes the total count of items in a shopping cart
const cartItemsSelector = state => state.cart.items;
const cartItemCountSelector = createSelector(
  cartItemsSelector,
  items => items.reduce((count, item) => count + item.quantity, 0)
);

// Define a component that uses the cart item count
const MyComponent = ({ cartItemCount }) => {
  console.log('Rendering MyComponent...');

  return (
    
      Cart item count: {cartItemCount}
    
  );
};

// Connect the component to the Redux store and map the cart item count to props
const mapStateToProps = state => ({
  cartItemCount: cartItemCountSelector(state)
});

export default connect(mapStateToProps)(MyComponent);

In the example above, we define a selector called cartItemCountSelector that computes the total count of items in a shopping cart. We use createSelector from the Reselect library to memoize the computation of the cart item count, based on the items in the cart.

We then define a component called MyComponent that uses the cart item count in its rendering. We also use console.log to log the component rendering.

Finally, we use the connect function from the react-redux package to connect the component to the Redux store, and the mapStateToProps method to map the cart item count to the cartItemCount prop.

By using memoization with Redux selectors, we can avoid unnecessary recomputation of derived data in our React Native app, and improve its performance.

5. Use Higher Order Components to Increase React Native App Speed

Higher Order Components (HOCs) are a useful feature in React Native that can be used to increase app speed by reusing code and improving code organization. They are functions that accept a component as an input and return a new component with enhanced capabilities.

Here are some ways you can use HOCs to improve React Native app performance:

  • Code reuse: HOCs allow for code reuse by encapsulating common functionality in a reusable component. This can aid in reducing the amount of code required to build and maintain your project.
  • Logic separation: HOCs help separate business logic from presentation logic. You can create an HOC that handles complex logic and pass it as a prop to your presentational component. This helps keep your components clean and easy to read.
  • Performance optimization: HOCs can be used to memoize expensive computations or to prevent unnecessary re-renders. This can boost your app’s performance by minimizing the amount of work required during each render cycle.
  • Authentication: HOCs can be used to handle authentication logic. For example, before displaying a component, you might write a HOC that checks to see if the user is authorized.
  • Data fetching: HOCs can also be used to fetch data from an API. You can create an HOC that fetches data and passes it as a prop to your component.

Example:

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

const withDataFetching = (WrappedComponent, dataSource) => {
  const WithDataFetching = (props) => {
    const [data, setData] = useState([]);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState('');

    useEffect(() => {
      setIsLoading(true);
      fetch(dataSource)
        .then(response => {
          if (response.ok) {
            return response.json();
          } else {
            throw new Error('Something went wrong');
          }
        })
        .then(data => {
          setData(data);
          setIsLoading(false);
        })
        .catch(error => {
          setError(error);
          setIsLoading(false);
        });
    }, []);

    return (
      
    );
  }
  
  return WithDataFetching;
};

export default withDataFetching;

In this functional component implementation, useState hook is used to manage the state of data, isLoading and error. When the component mounts, the useEffect hook retrieves data and updates the state with the response. Finally, using spread syntax, the WrappedComponent is returned with the state and props handed down as props.

6. Avoid Passing Functions Inline as Props

In React Native, passing functions inline as props can cause unnecessary re-rendering of components and degrade performance. This is because inline functions are recreated on every render, even if their implementation remains the same, which can cause child components to unnecessarily re-render.

Here’s an example of how passing an inline function as a prop can cause re-renders:

Avoid Anonymous Functions while Rendering

In the example above, we define a functional component called MyComponent that uses the useState hook to maintain a count state. We also define a Button component that increments the count state when clicked, using an inline function as the onPress prop.

However, because the inline function is recreated on every render, clicking the button will cause MyComponent to re-render, even though its implementation hasn’t changed. This can be wasteful and slow down the performance of the app.

To avoid this issue, it’s recommended to define functions separately and pass them as props, instead of defining them inline. This way, the functions will be created only once and reused across multiple renders, which can help in React Native performance optimization.

Here’s an example of how to define a function separately and pass it as a prop:

Copy Text
import React, { useState } from 'react';
import { Button } from 'react-native';
const MyComponent = () => {
  const [count, setCount] = useState(0);
  const handleIncrement = () => {
    setCount(count + 1);
  };
  console.log('Rendering MyComponent...');
  return (
    

In the example above, we define the handleIncrement function separately and pass it as the onPress prop of the Button component. Because the function is defined outside of the component’s rendering, it will only be created once and reused across multiple renders, which can help optimize performance of React Native app.

7. Optimizing JSON Data for React Native App Performance

Mobile applications always require load resources from a service or remote URL, and to accomplish such actions, programmers make fetch requests to pull data from that server.

The fetched data from private as well as public APIs returns in a JSON form that has some sort of compound nested objects. Usually, most of the programmers store the same JSON data for local offline access, and the performance suffers because JS applications render JSON data slowly.

So, it is advisable to convert raw JSON data into simpler objects before rendering.

Copy Text
fetch('SomeURL', {
 method: 'POST',
 headers: {
    Accept: 'application/json',
   'Content-Type': 'application/json',
 },
 body: JSON.stringify({
   firstParam: 'yourValue',
   secondParam: 'yourOtherValue',
 }),
}).then((response) => response.json())
  .then((responseJson) => {
       // Use JSON.parse Method To Convert Response in Object 
       var response = JSON.parse(responseJson);
       return response;
     })
   .catch((error) => {
     console.error(error);
   })

8. Use NativeDriver Animation

Animations in React Native look good and are easy to create. As the animated library lets you sanction the native driver, it will send the animations over the bridge to the native side before the animation starts.

Animations will execute the main thread independently of a blocked JavaScript thread; it will result in a smoother experience and fewer frame drops.

Change use Native Driver to the animation configuration. This is how a regular animation performs;

  • JavaScript driver uses requestAnimationFrame to execute on every frame
  • Intermediate values are calculated and passed to the View
  • The View is updated using setNativeProps
  • JS sends these changes to the native environment through the bridge
  • Native updates UIView or Android.View.

But, Native Driver Animation works like this.

  • The native animation driver uses CADisplayLink or android.view.Choreographer to execute on every frame
  • Intermediate values are calculated and passed to a view
  • The UIView/android.View is updated.
Copy Text
Example :
	Animated.timing(
            this.state.fadeAnim, {
            toValue: 1,
            userNativeDriver: true,
           }
        ).start()

< Animated.ScrollView // <-- Use the Animated ScrollView wrapper
  scrollEventThrottle={1} // <-- Use 1 here to make sure no events are ever missed
  onScroll={Animated.event(
    [{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }],
    { useNativeDriver: true } // <-- Add this
  )}
>
  {content}
< /Animated.ScrollView >

Leverage our result-oriented React Native development services to get rid of the technical headaches.
Being the most prominent React Native development company we will help you build exceptional mobile apps that lead you to Android Play and iOS app store.

9. Reduce Application Size

React Native uses external and component form libraries to impact application size. To reduce the size, you have to optimize the resources, use ProGaurd to create different app sizes for different device architectures, and compress the graphics elements, i.e., images.

You can follow below common practices for reducing app size and image size to improve the React Native app speed:

  • Move components from the native realm to the React Nativerealm.
  • The components on the JavaScript side use a bridge to communicate with the Native side.
  • Reduce the load on the bridge and improve the React Native app performance.
  • Check the stability of open-source libraries before you use them. Boilerplate codes in libraries often slow down the rendering performance.
  • Some components make heavy use of message queues while communicating with the Native side, so you should not pass them on to the main thread.
  • Use smaller-sized images and use PNG as opposed to JPG.
  • Convert your images to WebP format.
  • Make use of React NativeJS Thread and Navigator transitions.
  • Reduce CodePush bundle size by 66% with .webp format.

Final words

If you are convinced that your users require these personalized features in your existing app, React Native is your best friend. All you need is a renowned React Native development company to improve the App performance of React Native by implementing these practices to your application.

Bacancy Technology is a globally renowned software development company and a one-stop solution to Hire React Native developer to turn your ideas into a viable business solutions. Our offshore React Native app developers are well-versed in understanding global clients’ MVPs and have successfully delivered a wide range of products. Uplift your React Native app performance by optimizing your app with customized functionalities.

Frequently Asked Questions (FAQs)

To optimize the FlatList, you should:

  • Avoid using arrow functions inline for render item.
  • Not use 1080P HD images in the list.
  • Optimize maxToRenderPerBatch prop
  • Improve window size prop
  • Use getItem Layout

1. Avoid costly re-renders
2. Use < FlatList >, < SectionList >, < VirtualizedList > instead of < ListView >, < ScrollView >
3. Keep in mind to upgrade React Native to the newest version.
4. Decrease the Application Size.
5. Enhance your images.

As per the documentation, removeClippedSubviews is a special improvement property open to view by RCTView. It is useful for content scrolling when there are many subviews, most of which are offscreen.

PureComponent exclusively handles the shouldComponentUpdate method for you; the rest is similar to Component. PureComponent will simply compare when there is a change in props or state.

Are you Looking for Sure-shot Ways to Speed up React Native App Performance?

Experience improved performance and better visibility by maintaining an existing app or migrating to the latest version. Hire hand-picked React Native developers to keep your app up-to-date and avoid the most common pitfalls.

BOOK A 30 MIN CALL

Build Your Agile Team

Hire Skilled Developer From Us

[email protected]

Your Success Is Guaranteed !

We accelerate the release of digital product and guaranteed their success

We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.

How Can We Help You?