React Native

5 tips to improve the performance of your React Native application

5 tips to improve your React Native app performance

 

React Native is great for building applications fast: in 6 months we had everything we needed in terms of features. However, it took 5 seconds to display some pages which is way too much for users. When your application is slow, you have to learn how to become a performance expert. It is easier than you might think. 

The React Native documentation on performance

It is a good starting point to investigate performance. You will find several tips and best practices relative to performance:

  • It will give you information on the JS and UI threads of a React Native application
  • What could possibly be done in order to maintain a frame rate close to 60fps.
  • Explanations on the performance debugger (another article will follow to explain that).
  • Which components you have to use to improve performance (++code>FlatList++/code>, ++code>PureComponents++/code>, etc.)

 

bam tech react native redux

 

However, we needed more customised and detailed advice on performance, that is what will follow.

 

1. Do not request twice a minute a data that changes every 24h

 

If you know your data has not changed, do not request your back-end for updates!

 

That leads us to the first tip I will give to improve your React Native application performance. You do not need to perform a new request each time you open a page if you already have data stored in a redux store (or somewhere else) and if you know that those data have not changed.

We installed a caching system for our request: it is a middleware that detects every starting saga and if the request has already been realized in the cache duration that you have set, the request is intercepted and will not start. You will find our redux-actions-cache-middleware on github here.

 

Thanks to this, we have significantly reduced the displaying time of our page  (from 5s to 3s).

++table class="table table-striped table-bordered">++thead>++tr style="height: 26px;">++th style="height: 26px;">Without cache middleware++/th>++th style="height: 26px;">With cache middleware++/th>++/tr>++/thead>++tbody>++tr style="height: 202px;">++td style="height: 202px;">

bam tech react native redux

++/td>++td style="height: 202px;">

bam tech react native redux

++/td>++/tr>++tr style="height: 39px;">++td style="height: 39px; text-align: center;">Displaying time: 5s++/td>++td style="height: 39px; text-align: center;">Displaying time: 3s++/td>++/tr>++/tbody>++/table>

Fig1. - A middleware catching request will reduce the number of renders

 

2. Use simple selectors as much as possible, try to perform calculation when receiving data from a request

 

If you have to perform calculation, do it once and only if data has changed!

 

When you perform an API call, you receive data from your back-end that you will put in your Redux store. To use them, you are going to implement selectors to access these data. If your selector performs calculations, those calculations will occur each time your page renders because your selector will be called each time your component renders. It can cause performance issues, especially if you have multiple selectors performing large calculation at the same time.

There are ways to avoid that kind of problem:

  • Try to store only data that you are going to use in your Redux store.
  • If you need to perform calculation on your back-end response, try to perform them in a service and to call that service on this response and then store them. This way, the data you store will be exactly what you need to display.
  • If you really have to perform calculation in your selectors (sort data, etc.), try to use a library such as re-select. Thanks to that, you will be able to memoize the response of your selector if the data in your store have not changed.

 

++table class="table table-striped table-bordered">++thead>++tr>++th>Without re-select/adapter++/th>++th>With re-select/adapter++/th>++/tr>++/thead>++tbody>++tr>++td>

bam tech react native redux

++/td>++td>

bam tech react native redux

++/td>++/tr>++/tbody>++/table>

Fig2. - Use re-select to reduce the number of useless calculation

 

3. Only display what is useful to your user

 

If your user only needs 50 points, do not display 500!

When you are rendering resource intensive components such as graphs or arrays, you have to think that through by asking yourself this question: "which data do I need to display?". If the data you receive from your API call or your SQL request contains 200 points, it does not necessarily means that you have to display these 200 points.

For example if you only need half of the data, when performing your api call, create a small function that will only store half of the points in your redux store. We have implemented that change in our project and we were able to gain 2s in the displaying time of our pages.

 

++table class="table table-striped table-bordered" style="margin-left: auto; margin-right: auto;">++thead>++tr>++th>With 200 points++/th>++th style="text-align: center;">With 80 points++/th>++/tr>++/thead>++tbody>++tr>++td>

bam tech react native redux

++/td>++td>

bam tech react native redux

++/td>++/tr>++tr>++td style="text-align: center;">Displaying time: 4s++/td>++td style="text-align: center;">Displaying time: 2s++/td>++/tr>++/tbody>++/table>

 

4. Avoid useless rendering

Do not re-render a Component if it does not need to!

 

When a parent component is rendered, the children of this component will be re-rendered. It means that if you're performing a re-rendering of your component, it is because something has changed in your component. You can try to limit the re-rendering of components by using the ++code>shouldComponentUpdate++/code> function.

 

bam tech react native redux

 Fig3. - React Native Component life cycle.

Each time your component is unMounted, you have to ask yourself, is this what I want?

For example, on one of our application's page we were displaying tabs. Each tab was unmounted when the user was switching between tabs. This was a huge waste of resources because we needed to re-mount the component while the data had not changed. Thanks to that change, the tabs were rendered only once and we were able to navigate through them instantly.

 

bam tech react native redux

Fig4. - When your tab Component is not unmounted when you swich between tabs, the navigation becomes instant

 

5. Watch your technical debt

Accumulating technical debt also has an impact on performance!

 

Technical debt, the nightmare of every developer on a projet is also one of your worst enemy when it comes to performance. When you take a look at Facebook documentation on Performance, they do not say anything else: remove all useless console.logs, use ++code>FlatList++/code> instead of ++code>ListView++/code> for your large lists of elements, etc. These are concrete examples of technical debt.

In our project, we were using the LinePlot component from VictoryNative. We forgot to remove a useless label component on our graph. It was rendered for every points on the graph (which means hundreds of times) and that was causing us huge performance problems.

Do not hesitate to tackle your technical debt as often as possible, the performance of your application will thank you!

 

Next steps

We implemented these 5 tips on our project and the results were astonishing. The displaying time of our pages went from 3/4s to less than a second, thus exceeding our own expectations. It took us 3 to 4 days of development to realize all the actions, but it was worth the shot.

Conclusion

We were proud to have learnt so many things on performance but there are still many things left to learn on the subject: how to properly use the performance debugger, find ways to automatically check the front performance of an application, etc.

The subject is rich and fascinating and we are opened to discussion, so if you have any other ways to improve the performance of your application, feel free to share them with us!

Développeur mobile ?

Rejoins nos équipes