How to improve the performance of a React Native App?

See how you will boost your react native app performance, improve start-up time, decrease bundle size & memory usage.

Key Points -
  • Hermes
  • Avoid re-rendering using useMemo
  • Cache Images
  • Use nativeDriver with Animated Library
  • State Management With Redux/MobX/Apollo
  • Remove Console Log
  • Optimize Android App Size

1. Hermes

With Hermes your app will start at 2x speed, memory usage will decrease to 30%, android app bundle size decreased 30%, iOS Application performance will boost.

Let’s see how to integrate Hermes. Hermes supports iOS support after the 0.64 version. So make sure you upgrade your app using React Native Upgrade Helper.

Enabling Hermes -

Android -
Add following code line In android/app/build.gradle

project.ext.react = [
  entryFile: "index.js",
-     enableHermes: false  // clean and rebuild if changing
+     enableHermes: true  // clean and rebuild if changing
]

If you're using ProGuard, add following rules in proguard-rules.pro :

-keep class com.facebook.hermes.unicode.** { *; }
-keep class com.facebook.jni.** { *; }

Clean the build

cd android && ./gradlew clean

iOS -
Edit ios/Podfile file with below code lines -

use_react_native!(
  :path => config[:reactNativePath],
  # to enable hermes on iOS, change false to true and then install pods
-    :hermes_enabled => false
+    :hermes_enabled => true
)

Install the Hermes pod

cd ios && pod install

That’s it, Now create your iOS & Android build and see application start-up speed, memory usage, and specially bundle size.

2. Avoid re-rendering using useMemo

In the sample below I have taken FlatList and Button. First time Flatlist renders perfectly. Now when user pressed button new setCount will update state variable and whole component getting reloaded with FlatList even no values updated in Array. To avoid this I have wrapped FlatListItem (UseMemoListItem) with useMemo, so useMemo checks if any change in props then only it will render the JSX else it will return render & return the view previous props.

const technology = [
  { name: 'React Native' },
  { name: 'React.js' },
  { name: 'Next.js' },
 ];
 const [arrTechnology, setArrTechnology] = useState(technology);
 const [count, setCount] = useState(0);

 function UseMemoListItem({item, onChange, arrTechnology}) {
  return useMemo(() => {
    return (
      <View style={Styles.container}>
        <Text>{item.name}</Text>
      </View>
    );
  }, [item.status]);
 }

  return (
    <View style={Styles.container}>
      <Button title='Increment' onPress={() => setCount(count + 1)} />
      <FlatList
        data={arrTechnology}
        keyExtractor={(item) => String(item.name)}
        renderItem={({item, index}) => (
          <UseMemoListItem
            item={item}
          />
        )}
        ItemSeparatorComponent={() => <UseMemoListItemSeprator />}
        showsVerticalScrollIndicator={false}
      />
    </View>
  );

3. Cache Images

React Native Image component allow developers to display Images in application, still there is few issues like -

  • Rendering number of Image (Product List - ECommerce Application)
  • Low performance in Caching Image Loading
  • Image flickering

To solve this issue React Native support in-build caching for iOS by enabling following lines of code.

<Image
  source={{
    uri: 'https://example.com/image.png',
    cache: 'only-if-cached'
  }}
  style={{ width: 400, height: 400 }}
/>

But, what to do for Android, there is a popular third-party library named - react-native-fast-image which will work perfect for iOS & Android. Using Fast Image you can give quick image rendering, caching mechanism and many more to application users.

import FastImage from 'react-native-fast-image'

const YourImage = () => (
    <FastImage
        style={{ width: 200, height: 200 }}
        source={{
            uri: 'https://yourimageurl.com/image.png',
            headers: { Authorization: 'token' },
            priority: FastImage.priority.normal,
        }}
        resizeMode={FastImage.resizeMode.contain}
    />
)

4. Use nativeDriver with Animated Library

We are using animations in our Application, but sometimes it will not run smoothly as expected which impacts application render performance. To avoid flicker and run smooth animation, useNativeDriver which send animation to native bridge before animation start on component. This helps animations to be executed on separate javascript instance, which resulting in smoother animation.

It’s very simple to integration - useNativeDriver: true

Animated.timing(this.state.animatedValue, {
  toValue: 1,
  duration: 500,
  useNativeDriver: true, // <-- Add this
}).start();

5. State Management With Redux/MobX/Apollo

Many times it is required to manage data locally means caching data which shows immediately to the user without interruption whenever the user comes back to the application. We are using AsyncStorage, Local Database Storage to store data and when the user comes back next time/ open application next time we are fetching data and keeping it in Global variables to access anywhere in the application.

To manage this in various screens and storing data into various arrays, object Prefer popular State Management Library like Redux, Redux Rematch, MobX and Apollo. These library will storage | mange | retrieve data for you and you can easily access throughout the app without any interruption.

6. Remove Console Log

All we are using console.log('Hello KPITENG!') to debug applications. While deploying an application if we keep console.log() then it will create performance issues due to javascript thread.

To remove console.log in Production, following simple installation and setup.

npm install babel-plugin-transform-remove-console

Now, modify the .babelrc file to remove the console statements, as shown below:

{
  "env": {
    "production": {
      "plugins": ["transform-remove-console"]
    }
  }
}

7. Optimize Android App Size

React Native Android apps contain -

  • Resources such as images, fonts, etc.
  • Four different binaries compiled for different CPU architectures
  • Javascript bundle with business logic
  • Other in-built files

While you are building an app this will combine all these and create a binary for you. To optimize binary size Android build in React Native by adding below line

Update following line in android/app/build.gradle

def enableProguardInReleaseBuilds = true

If you want to extract four different binary according your CPU architecture then,

def enableSeparateBuildPerCPUArchitecture = true

Thanks for reading Blog!

KPITENG | DIGITAL TRANSFORMATION
www.kpiteng.com/blogs | [email protected]
Connect | Follow Us On - Linkedin | Facebook | Instagram

32