Skip to main content

Optimizing Performance

1. Using React.memo

React.memo is a higher-order component that memoizes the result of a functional component to prevent unnecessary re-renders when the props remain unchanged.

Property Description Default
Component The functional component to memoize. -
compareFunction Optional function to customize the comparison logic. Shallow equality check

Example: Basic Usage

import React, { memo } from 'react';

const ExpensiveComponent = memo(({ count }) => {
  console.log("ExpensiveComponent rendered");
  return <div>Count: {count}</div>;
});

export default ExpensiveComponent;

Example: Custom Comparison Function

import React, { memo } from 'react';

const CustomComponent = memo(({ data, title }) => {
  return <div>{title}: {data}</div>;
}, (prevProps, nextProps) => {
  return prevProps.data === nextProps.data;
});
Note: React.memo only checks props for equality. It does not prevent re-renders due to state changes or context updates in parent components.

2. Implementing shouldComponentUpdate

Method Description Return Value Usage
shouldComponentUpdate Controls whether a component should re-render true to update, false to skip Override in class components to optimize rendering

Example: shouldComponentUpdate implementation

class MyComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // Skip re-render if props or state haven't changed
    return nextProps.data !== this.props.data ||
           nextState.count !== this.state.count;
  }

  render() {
    return <div>{this.props.data} - {this.state.count}</div>;
  }
}
Note: shouldComponentUpdate is not called for the initial render or when using forceUpdate().
Warning: Incorrectly implementing shouldComponentUpdate can lead to inconsistent UI or missed updates.

3. Using FlatList Optimization Props

Prop Description Values Notes
data Array of data to render array Required
renderItem Function that renders a single item function Required
keyExtractor Function to extract a unique key for each item function Recommended for performance
initialNumToRender Number of items to render initially number Default: 10
maxToRenderPerBatch Maximum number of items to render per batch number Default: 10
windowSize Number of items to render beyond the visible area number Default: 21
removeClippedSubviews Removes clipped subviews from the view hierarchy true or false Default: true
disableVirtualization Disables virtualization for FlatList true or false Default: false
getItemLayout Function to calculate item layout function Improves performance for fixed-height items

Example: Basic FlatList Usage

import React from 'react';
import { FlatList } from 'react-native';

const renderItem = ({ item }) => (
  <View style={{ padding: 10 }}>
    <Text>{item.name}</Text>
  </View>
);

const MyFlatList = () => {
  const data = [
    { id: '1', name: 'Item 1' },
    { id: '2', name: 'Item 2' },
    { id: '3', name: 'Item 3' },
  ];

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={item => item.id}
    />
  );
};

Example: Optimized FlatList with windowSize

import React from 'react';
import { FlatList } from 'react-native';

const renderItem = ({ item }) => (
  <View style={{ padding: 10 }}>
    <Text>{item.name}</Text>
  </View>
);

const OptimizedFlatList = () => {
  const data = Array.from({ length: 1000 }, (_, i) => ({
    id: i.toString(),
    name: `Item ${i}`,
  }));

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={item => item.id}
      windowSize={5}
      maxToRenderPerBatch={10}
      initialNumToRender={10}
    />
  );
};

4. Avoiding Inline Functions and Objects

Pattern Description Impact Better Alternative
inline function in render listItems = data.map((item) => <ListItem key={item.id} onPress={() => handlePress(item)} />); Creates new function instance on every render const handlePress = (item) => { /* logic */ }; listItems = data.map((item) => <ListItem key={item.id} onPress={() => handlePress(item)} />);
inline object in render <View style={{ backgroundColor: 'red', padding: 10 }}> Creates new object instance on every render const viewStyle = { backgroundColor: 'red', padding: 10 }; <View style={viewStyle}>
inline array in render <FlatList data={data} renderItem={({ item }) => <Text style={[{ color: 'blue' }, item.style]}>{item.text}</Text>} /> Creates new array instance on every render const textStyle = [{ color: 'blue' }]; <FlatList data={data} renderItem={({ item }) => <Text style={[textStyle, item.style]}>{item.text}</Text>} />

Example: Avoiding Inline Functions

const MyComponent = ({ items, onPress }) => {
  // ❌ Bad: Inline function creates new instance each render
  const renderItem = ({ item }) => (
    <TouchableOpacity onPress={() => onPress(item)}>
      <Text>{item.title}</Text>
    </TouchableOpacity>
  );

  return <FlatList data={items} renderItem={renderItem} />;
};

// ✅ Good: Move function outside component
const MyComponent = ({ items, onPress }) => {
  const handlePress = (item) => onPress(item);

  const renderItem = ({ item }) => (
    <TouchableOpacity onPress={() => handlePress(item)}>
      <Text>{item.title}</Text>
    </TouchableOpacity>
  );

  return <FlatList data={items} renderItem={renderItem} />;
};

Example: Avoiding Inline Objects

const MyComponent = ({ title }) => {
  // ❌ Bad: Inline object creates new instance each render
  return <View style={{ backgroundColor: 'red', padding: 10 }}>
    <Text style={{ fontSize: 16, color: 'white' }}>{title}</Text>
  </View>;
};

// ✅ Good: Define styles outside component
const containerStyle = {
  backgroundColor: 'red',
  padding: 10,
};

const textStyle = {
  fontSize: 16,
  color: 'white',
};

const MyComponent = ({ title }) => (
  <View style={containerStyle}>
    <Text style={textStyle}>{title}</Text>
  </View>
);

5. Using useCallback and useMemo

Example: Using useCallback for event handlers

import React, { useCallback } from 'react';

const Button = ({ onClick }) => {
  return <button onClick={onClick}>Click me</button>;
};

const ParentComponent = () => {
  const handleClick = useCallback(() => {
    console.log('Button clicked');
  }, []);

  return <Button onClick={handleClick} />;
};
Hook Description Use Case
useCallback Memorizes a function to prevent unnecessary re-renders Passing functions as props to child components
useMemo Memorizes a value to prevent expensive recalculations Expensive computations or object creation

Example: Using useMemo for expensive calculations

import React, { useMemo } from 'react';

const ExpensiveComponent = ({ data }) => {
  const expensiveValue = useMemo(() => {
    // Simulate expensive calculation
    return data.reduce((sum, item) => sum + item.value, 0);
  }, [data]);

  return <div>Sum: {expensiveValue}</div>;
};
Note: Both hooks should only be used when necessary to avoid performance overhead.

6. Implementing Image Optimization

Example: Using React Native's Image Component with Resize Mode

import React from 'react';
import { Image, StyleSheet } from 'react-native';

const OptimizedImage = () => {
  return (
    <Image
      source={{ uri: 'https://example.com/image.jpg' }}
      style={styles.image}
      resizeMode="cover"
    />
  );
};

const styles = StyleSheet.create({
  image: {
    width: 300,
    height: 200,
  },
});

export default OptimizedImage;
Property Description Values
resizeMode How to resize the image when it doesn't fit its container cover, contain, stretch, repeat, center
source Image source (URI or local resource) { uri: "url" }, { require("./image.png") }
blurRadius Applies a blur effect to the image number
Note: For best performance, always provide images in appropriate sizes and formats (e.g., WebP or AVIF).

Example: Preloading Images

import React, { useEffect } from 'react';
import { Image } from 'react-native';

const preloadImage = (uri) => {
  Image.prefetch(uri);
};

const MyComponent = () => {
  useEffect(() => {
    preloadImage('https://example.com/image.jpg');
  }, []);

  return <Image source={{ uri: 'https://example.com/image.jpg' }} />;
};

Image Optimization Best Practices

  • Use appropriate image sizes to avoid unnecessary loading
  • Preload images that are likely to be displayed soon
  • Use resizeMode to control how images are scaled
  • Consider using blurRadius for a smoother loading experience

7. Reducing Bundle Size

Example: Using React Native's Built-in Code Splitting

import { lazy, Suspense } from 'react';
import { ActivityIndicator, View } from 'react-native';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback=<View style={{ flex: 1, justifyContent: 'center' }}><ActivityIndicator /></View>>
      <HeavyComponent />
    </Suspense>
  );
}
Technique Description Impact Notes
Code Splitting Splitting code into smaller chunks to load only what's needed. Reduces initial bundle size Use lazy and Suspense for dynamic imports
Tree Shaking Removing unused code from the bundle during build. Reduces bundle size Works best with ES6 modules and proper build tools
Image Optimization Using appropriate image formats and sizes. Reduces asset size Use require for static assets and resizeMode for dynamic images
Minification Compressing JavaScript and CSS files. Reduces file size Enabled by default in production builds
Note: Bundle size can be analyzed using tools like react-native-bundle-visualizer.
Warning: Avoid importing large libraries directly; instead, use specific imports or lazy loading.

8. Using Hermes Engine

Note: Hermes is a JavaScript engine optimized for React Native apps, designed to improve app startup time, reduce memory usage, and enhance overall performance.
Feature Description Status
Hermes Engine JavaScript engine optimized for React Native apps NEW
Startup Time Reduces app startup time by pre-compiling JavaScript to bytecode NEW
Memory Usage Reduces memory consumption compared to JavaScriptCore NEW
Bytecode Compilation Compiles JavaScript to bytecode at build time NEW

Example: Enabling Hermes in React Native

// In android/app/build.gradle
project.ext.react = [
    entryFile: "index.js",
    enableHermes: true  // Enable Hermes
]

// In ios/Podfile
use_react_native!(
  :hermes_enabled => true
)

Benefits of Hermes

  • Improved app startup time
  • Reduced memory footprint
  • Better JavaScript execution performance
  • Enhanced app responsiveness

Hermes vs JavaScriptCore

  • Hermes: Optimized for mobile, faster startup, lower memory usage
  • JavaScriptCore: Default engine, more mature but less optimized for mobile

Enabling Hermes in React Native

  1. Update android/app/build.gradle to enable Hermes
  2. Update ios/Podfile to use Hermes
  3. Rebuild the app for Android and iOS
  4. Verify performance improvements using profiling tools

9. Implementing Code Splitting

Example: Using React.lazy and Suspense

import React, { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback="<div>Loading...</div>">
      <HeavyComponent />
    </Suspense>
  );
}
Feature Description Notes
React.lazy Lazy loads components Must be used with Suspense
Suspense Handles loading states Wraps lazy-loaded components
Note: Code splitting with React.lazy is supported in React 16.6+.

10. Profiling with React DevTools

Example: Enable Profiling in React DevTools

// In your app entry point
import { AppRegistry } from 'react-native';
import { App } from './App';

// Wrap your app with the profiling provider
const ProfilingApp = () => {
  return <App />;
};

AppRegistry.registerComponent('MyApp', () => ProfilingApp);
Feature Description Availability
React DevTools Profiler Measures component render times and identifies performance bottlenecks NEW React Native 0.72+
Render Duration Time spent rendering a component in milliseconds Available in all versions
Re-renders Counts how many times a component re-renders Available in all versions
Note: Profiling is only available in development builds. It has minimal impact on production performance.

Steps to Profile Performance

  1. Enable Developer Menu in your app (shake device or press Ctrl+M)
  2. Navigate to Debugging and select Start Profiling
  3. Perform actions that you want to analyze (e.g., scrolling, tapping)
  4. Stop profiling and review the results in the React DevTools

Profiling vs. Other Tools

  • React DevTools Profiler: Built-in, easy to use, good for component-level analysis
  • Flipper: More advanced, supports network and storage inspection
  • Chrome DevTools: For web-based React Native apps, limited support for native components

11. Optimizing Navigation Performance

Property Description Notes
initialRouteName Specifies the route to navigate to on first render Improves startup performance by avoiding unnecessary navigation
screenOptions Global options for all screens in navigator Can reduce re-renders by avoiding inline object creation
lazy Enables lazy loading of screens Improves initial load time by deferring screen rendering

Example: Using lazy loading

import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

<Stack.Navigator>
  <Stack.Screen name="Home" component={HomeScreen} />
  <Stack.Screen name="Profile" component={ProfileScreen} lazy={true} />
</Stack.Navigator>
Note: Lazy loading is particularly effective for screens with heavy assets or complex UIs.
  • Standard Navigation: All screens rendered immediately on app launch
  • Lazy Loading: Screens rendered only when accessed

12. Reducing Re-renders

Technique Description Usage
React.memo Prevents unnecessary re-renders of functional components <const MemoizedComponent = React.memo(MyComponent);>
useMemo Memorizes expensive calculations to avoid recomputation <const expensiveValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);>
useCallback Memorizes function definitions to prevent re-creation on every render <const handleClick = useCallback(() => { console.log('clicked'); }, []);>
shouldComponentUpdate (class components) Control re-rendering in class components <shouldComponentUpdate(nextProps, nextState) { return nextProps.id !== this.props.id; }>
Note: Use React.memo with useCallback for props to prevent unnecessary re-renders.

Best Practices

  • Wrap components with React.memo if they are pure
  • Use useCallback for event handlers passed to child components
  • Use useMemo for expensive computations

Was this article helpful?