React Native App Architecture

React Native is based on JavaScript, in which the React Native part interacts with the Native thread through a bridge. So, the React Native architecture has two significant sections:

1. Native Part built on Java, Swift, or Objective-C.

2. React Native part built on JavaScript.

To improve your React Native application performance, here are some common practices, which will surely help you a lot.

Memory Leakage Issue

Memory Leakage Issue

This is one the most common issues for native applications with a lot of unnecessary processes running in the background.

With the use of Xcode you can find a leaks

  • Go to XCode → Product → Profile (⌘ + i)
  • After that shows you all template choose leaks.

With the use of Android Studio can Find a leaks

  • Run React Native app normally (react-native run-android)
  • Run Android Studio
  • On the menu, click Tools → Android → Enable ADB Integration
  • Click Tools → Android → Android Device Monitor
  • When Android Device Monitor shows up, click Monitor → Preferences

Perf Monitor is a good choice to use for android leaks monitoring.

Import PerfMonitor from 'react-native/Libraries/Performance/RCTRenderingPerf';
PerfMonitor.toggle();
 PerfMonitor.start();
 setTimeout(() => {
   PerfMonitor.stop();
 }, 20000);
}, 5000);

To fix memory leaks in native applications, you can use scrolling lists such as FlatList, SectionList, or VirtualList instead of Listview.

Scrolling list also helps in smoothing the infinite scroll pagination, so if you’re building an application with a pull to refresh feature, and large data points, then it is advisable to consider it.

Reduce Image size

Reduce Image size

Images is a big issue for high memory usage. Image optimization is very impotent in React Native application.

1. Use smaller sized images.
2. Use PNG as opposed to JPG.
3. Convert your images to WebP format.

Why WebP Format?

  • WebP images can speed up your image loading time to 28%.
  • Reduced CodePush bundle size by 66% with .webp format
  • Helps to reduce iOS and Android binary sizes by 25% with.webp format
  • React Native JS Thread feels so much faster
  • Navigator transitions are so much smoother

Cache the images locally

Image caching is important for loading images faster. As of now, React Native only provides image caching support on iOS.

This is a quick example, as seen in the docs (IOS only)

	< Image
  		source={{
    			uri: 'https://facebook.github.io/react/logo-og.png',
    			cache: 'only-if-cached',
 		 }}
 	 	style={{width: 400, height: 400}}
	/ >

For Android, there are some npm libraries available out there that helps to solve the image caching issue. Unfortunately, they don’t offer the best performance.

Avoiding Unnecessary Renders

Overriding a Component Update:

One of the most common problems is not to implement this diff state, lifecycle and props yourself for the first- you should first ensure that if your components should update or not, and make sure to not pass too much needless work to Reconciler, probably it will drop your JS thread’s FPS. Though outspread from PureComponent instead of Component.

shouldComponentUpdate( nextProps, nextState ) { 
	if(nextProps.isUpdated == this.props.isUpdated) {
          return false; 
        } else {
	  return true; 
        }
  }

shouldComponentUpdate should always return a boolean  an answer to the question,
“should I re-render?”
Yes, it is advisable to have little component.

Using PureComponent :

PureComponent in React don’t have a state, they just render your component based on the data passed via props. It re-renders only if props change.

shouldComponentUpdate life-cycle method is used in regular non pure React Component to cancel the re-render by returning false in certain scenarios.

class MyPureComponent extends React.PureComponent {
	render() {
		return (
                     Hello Bacancy
                )
        }
}

Optimizing JSON Data

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, what I would like to advise you is convert raw JSON data in simpler objects before rendering.

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);
   })

Use NativeDriver Animation

Animations in React Native look good and are easy to create. As animated library lets you sanction 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. Set 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 this changes to the native environment through the bridge
  • Native updates UIView or android.View

But, Native Driver Animation working 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
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 >

Reduce Application Size

In React native you use external libraries and component form libraries so it impact in application size.

To reduce the size, you have to optimize the resources, use ProGaurd, create different app sizes for different device architectures, and make sure to compress the graphics elements, i.e. images.

You can follow below common practices for reducing app size:

  • Move components from the native realm to the React Native realm
  • The components on the JavaScript side use a bridge to communicate with the Native side.
  • Reduce the load on the bridge and improve the app’s 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 the main thread.

About the Author

Niraj Chauhan
I am React-Native software engineer and enthusiastic about developing Mobile apps. I love to explore various mobile apps and keep myself updated with latest ongoing trends in the market.