Improving your React Native application performance with react-navigation-focus-render
Tags: reactnative, react, showdev, opensource
Published: 2022-02-20 15:25:46

---
title: Improving your React Native application performance with react-navigation-focus-render
published: true
description: Prevent React Native re-renders on inactive screens
tags: reactnative, react, showdev, opensource 
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vupba6olmcmwsy0jjwor.png
---

Performance in React Native has always been a bit of a battle, great performance is achievable but is a lot more sensitive to unoptimized code than traditional web development.

# Background

I'd recently discovered an issue in my application where my home tab screen had a collection of components that contained multiple re-renders. 

The problem was quite straightforward to resolve, but during this, I found out that this non-performant screen was slowing down the other tabs within my application.

# A simple example of why this happened

Let's make a simple example that replicates this issue.

**ExpensiveComponent**

Here's our component that's causing the issue, every time this renders we'll see an obvious performance hit. It's connected to redux and will re-render whenever the state changes for the count.

```
const ExpensiveComponent = () => {
  const {count} = useSelector((state) => ({
    count: state.count,
  }));
  return  (
    <>
      {!!count && <Text>Count is {count}</Text>}
      {new Array(5000).fill(0).map((v, k) => (
        <Text key={k}>{v}</Text>
      ))}
    </>
  );
};

```

**HomeScreen**

Our home screen renders the expensive component and lets us go to screen 2. As long as ExpensiveComponent is re-rendering we will see an obvious performance hit.

```
const HomeScreen = () => {
    const navigation = useNavigation();
    const goScreen2 = ()=>{
        navigation.navigate('Screen2')
    }
    return (
        <>
            <Button
                title={'Go to Screen 2'}
                onPress={goScreen2}
            />
            <ExpensiveComponent />
        </>
    );
};
```

**Screen2**

Our second screen has no performance issues by itself, it contains a button that dispatches an action to update the count. It doesn't render much and you'd expect pressing the button and displaying an updated count to be immediate.


```
const Screen2: React.FC<ComponentType> = ({}) => {
    const {count} = useSelector((state) => ({
        count: state.count,
    }));
    const dispatch = useDispatch();
    const setCount = useCallback(
        (data: number) => {
            return dispatch(AppActions.setCount(data));
        },
        [dispatch],
    );
    const onPress = ()=> {
        setCount((count || 0) + 1)
    }
  return (
      <Button
        onPress={onPress}
        title={`Update Count (${count || 0})`}/>
  );
};
```

You would expect Screen2 to have no performance issues right? Wrong. Pressing the update count button was consistently blocking the UI by around 250ms, this can be seen by using a tool I made, [react-native-performance-monitor](https://github.com/Flagsmith/react-native-performance-monitor).

![React Native Performance Monitor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tymk379b2g5oed0d42c2.png)
 
# So why did this happen?

The reason made sense, other tabs were using updating the state that was also used on the HomeTab and as it turned out, inactive tabs will re-render even if they aren't shown.

Even with optimized components, the fact that this happens is useful to be aware of. If you have 5 tabs in your app that have been visited in a session, any state updates can trigger re-renders on all of them.

# Preventing this behavior with [react-navigation-focus-render](https://github.com/Flagsmith/react-navigation-focus-render)

This is where my new npm package comes in. By wrapping the render our ExpensiveComponent in <FocusRender, children are not re-rendered until the screen is focused

```
const ExpensiveComponent = () => {
  const {count} = useSelector((state) => ({
    count: state.count,
  }));
  return  (
    **<FocusRender>**
      {!!count && <Text>Count is {count}</Text>}
      {new Array(5000).fill(0).map((v, k) => (
        <Text key={k}>{v}</Text>
      ))}
    **</FocusRender>**
  );
};

```

Simply by adding this, our example is more performant. Here's the comparison to prove it:

![Focus Render](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axy5i8s1ny7b1didn0sm.png)

This shows an average render time of around 6ms vs the original 250ms.
 
# How does this work?

It's quite simple, This module works by preventing screen re-renders of inactive screens until that screen is focused.

The entire code can be found [here](https://github.com/Flagsmith/react-navigation-focus-render/blob/main/lib/index.js). It uses the useIsFocused() hook provided by react-navigation combined with a classic shouldComponentUpdate, returning true only if the screen is focused.


To illustrate this clearly, here's how the above example behaves.

![Example](https://recordit.co/r6UiKKtlQ6.gif)

As you can see, it's noticeable when this re-render occurs due to how expensive the component is. However, in cases less extreme, it's more likely to behave like the following:

![Example2](https://recordit.co/XSq5RbuoQ1.gif)

# Conclusion

Although this library should not replace optimizing components,  I believe with big applications this library can introduce big performance benefits. 

Let me know if you find this useful, you can check it out on [GitHub](https://github.com/Flagsmith/react-navigation-focus-render) :) 

Happy Hacking!