Daniel Idaszak React Native Developer w Netguru.

Improve React Native performance with immutability

3 min read 927

A guide for improving React Native performance with immutability.

How to measure performance in React Native

React Native SlowLog is a tool suitable for bigger performance problems in React Native.

It should take priority over many other React Native performance tools because it can notify you if some operations in your app are slow.

The most common way to measure performance in React Native apps is to use a built-in Performance Monitor.

You can open it from the debug menu in your simulator by clicking the Perf Monitor. It’ll be displayed in your app over the presently opened screen.Improve React Native performance with immutability.
The RAM column shows memory usage for the current process, while the second column displays JavaScript thread memory usage.

The Views column has two numbers: the first number shows the count of currently visible views, and the second displays view count created and saved in the memory.

The purpose of the last two columns is to show the current frame-rate for the user interface and frames per second for the JavaScript thread.

It’s important to check your performance in production mode. Make sure to check the application log for:

__DEV__ === false, development-level warning are OFF, performance optimizations are ON

When it comes to Android performance measurement, React Native docs recommend using systrace instead.

Another tool you can use, which is also React Native compatible, is Why-did-you-render. It will notify you if there are avoidable re-renders. It’s also capable of tracking React Hooks issues.

Avoid mutations and track them

We should avoid changing data that should be immutable, e.g. Redux state. It allows us to avoid unnecessary re-renders and enables advanced memoization.

You can find great recipes for immutable code in this great blog post.

It’s easy to make a simple mistake, especially since JavaScript is not a completely functional programming language and it doesn’t favor immutability.

There are many JavaScript methods you should avoid while aiming in immutable code. Check out this list of mutator methods.

In Mozilla docs, you can find JavaScript methods that are safe to use.

But how do you check your code for mistakenly mutated Redux store? We can use a middleware called redux-immutable-state-invariant, which is designed specifically for this purpose.

This library should not be used in production, as it can degrade app performance, but It’s a great tool for tracking possible issues.

The library is very easy to use because the set up is one simple step.

How does It work? If you’re in your app and some data is mutated either in dispatch or between dispatches, you’ll receive an error message.

Use pure components

If you require small updates of immutable structures such as state, using pure components is the way to go.

Pure components were added with React 16.6, and they’re a great way to improve performance by avoiding unnecessary re-renders.

It works out of the box and is no different from React components. The only change is that it has a shallow prop and state comparison.

Keep in mind that if you need to update structure with deep comparison, it may be quicker to re-render the whole component. It’s important not to overdo it with pure components.

Remember, that pure component’s shallow equality check is not cheap, and we should always measure performance difference instead of putting them everywhere.

For a deeper explanation of pure components in React, you can look at this blog post: Pure functional components.

Use Memo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

The other way to improve performance by avoiding re-renders is to use useMemo hook, which returns a memoized value.

Functions passed to useMemo will be run only when some argument passed as array changes.

While improving performance, we should always avoid early optimization.

When using useMemo we can easily fall into a trap. It’s a great way to improve performance for components working without it, but we should know that React can decide to recalculate values on re-render anyway – e.g. while freeing memory.

How to work with deeply nested structures

When working with deeply structures, it’s better to use something more powerful like the Moize library.

To make usage more pleasant and useMemo-like, we can follow documentation and prepare a useMoize Hook like this: https://github.com/planttheidea/moize#usemoize-hook

const deepSum = useMoize(obj => obj.a + obj.b, [object], {
  isDeepEqual: true,
});

We should avoid deep comparison, but in some cases, we can use the option isDeepEqual to memoize our structure.

To improve performance, we need to be sure that we are avoiding mutations.

It’s especially hard when changing deeply nested structures. In such case, one of your best options is to use Immer library, which is extremely easy to grasp:

import produce from "immer"
 
const baseState = [
  {
    todo: "Measure performance",
    done: true
  },
  {
    todo: "Improve code",
    done: false
  }
]
 
const nextState = produce(baseState, draftState => {
  draftState.push({todo: "Use Immer"})
  draftState[1].done = true
})

Conclusion

Writing code in immutable fashion can save you a lot of trouble, so it’s worth knowing which JavaScript methods are considered unsafe.

If you find that your app isn’t performing well, you can choose from various tools. However, you should check React Native SlowLog first.

Built-in tools like Performance Monitor can also be very helpful.

The slow components can be improved by using new React features such as Pure Components, but as stated in this article, it should be used carefully.

Remember the famous saying among software developers: “Premature optimization is the root of all evil”.

For performance problems with deeply nested structures, you should aim to achieve immutability with external libraries such as Moize or Immer.

Plug: , a DVR for web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Daniel Idaszak React Native Developer w Netguru.

Leave a Reply