Optimizing Performance in React Native Apps (Expo)
Vincent Odukwe
Posted on October 10, 2024
Expo simplifies the React Native development process by providing a managed workflow, handling many native configurations for you. However, with the added convenience, it's still essential to optimize your app to ensure smooth and efficient performance, especially when dealing with large data, animations, or resource-heavy components. In this guide, we'll cover practical ways to improve your Expo app's performance.
1. Use the Right Image Sizes and Formats
Unoptimized images are one of the biggest culprits when it comes to performance issues. In Expo, you can manage your image resources effectively with these tips:
- Resize images before using them in your app. Large images increase memory usage.
- Use WebP format for better compression compared to PNG or JPEG. Expo supports WebP on both iOS and Android.
Example with an optimized image:
import { Image } from 'react-native';
<Image source={{ uri: 'https://example.com/myimage.webp' }} style={{ width: 100, height: 100 }} />;
Expo also has a built-in Asset
module for preloading images, improving perceived performance by loading images before they're rendered:
import { Asset } from 'expo-asset';
Asset.loadAsync(require('./assets/myimage.webp'));
2. Use React.memo
for Performance
Using React.memo
in Expo can help prevent unnecessary re-renders of components. This is useful when your component does not need to update unless its props change.
const MyComponent = React.memo(({ title }) => {
return <Text>{title}</Text>;
});
This is especially beneficial when working with components inside FlatList
or ScrollView
, where re-renders can occur frequently.
3. Optimize Lists with FlatList
In Expo, always opt for FlatList
or SectionList
when rendering large lists. These components only render items that are visible on the screen, reducing memory usage and improving performance.
Key optimizations:
-
Use
getItemLayout
: Pre-calculate item sizes to reduce render times. -
Control
initialNumToRender
: Reduce the number of initially rendered items.
Example:
<FlatList
data={myData}
renderItem={({ item }) => <Text>{item.name}</Text>}
keyExtractor={(item) => item.id}
getItemLayout={(data, index) => ({ length: 50, offset: 50 * index, index })}
/>
4. Optimize Animations with Reanimated
and useNativeDriver
Animations can significantly impact performance if not handled properly. Expo provides out-of-the-box support for react-native-reanimated
, which offers better performance over the default Animated
API.
To ensure that animations run smoothly, use the useNativeDriver
option to offload animation calculations to the native thread.
import { Animated } from 'react-native';
Animated.timing(animationValue, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
For more complex animations, use react-native-reanimated
, which is also compatible with Expo.
5. Optimize Bundles with expo-splash-screen
and expo-asset
Loading large assets during app launch can slow down startup times. To manage this efficiently, use expo-splash-screen
and expo-asset
to preload images and other assets.
In your App.js
, configure your splash screen like this:
import { SplashScreen } from 'expo-splash-screen';
import { Asset } from 'expo-asset';
SplashScreen.preventAutoHideAsync();
const cacheResourcesAsync = async () => {
const images = [require('./assets/icon.png')];
const cacheImages = images.map(image => Asset.loadAsync(image));
await Promise.all(cacheImages);
SplashScreen.hideAsync();
};
cacheResourcesAsync();
This ensures that the app preloads assets before the user interacts with it.
6. Use Hermes with Expo (Bare Workflow)
As of now, Hermes is only available in the Bare Workflow of Expo. If you're using the Bare Workflow for more control over native code, enabling Hermes can provide better JavaScript execution performance, reduce memory usage, and improve startup times.
To enable Hermes in Expo Bare Workflow:
- Open
android/app/build.gradle
and add this:
project.ext.react = [
enableHermes: true
]
- Rebuild the Android app using:
expo run:android
While the Managed Workflow doesn’t yet support Hermes, Expo is working on adding this functionality.
7. Avoid Unnecessary Re-renders
Unnecessary re-renders in Expo can impact performance. A few tips:
-
Avoid inline functions and styles: Define functions and styles outside your
render
method to prevent creating new instances every render.
Example:
const styles = StyleSheet.create({
button: {
backgroundColor: 'blue',
},
});
const MyComponent = () => (
<Button title="Click me" style={styles.button} />
);
- Use
useMemo
anduseCallback
to memoize values and functions to avoid unnecessary recalculations.
8. Optimize Expo Push Notifications
Using push notifications via Expo can sometimes increase background processing, especially with frequent or heavy notifications. To mitigate performance issues:
- Limit the frequency of notifications: Use server-side logic to batch or throttle notifications.
- Use background notifications: These allow for reduced activity when the app is inactive.
Set up notifications in Expo using the expo-notifications
package:
import * as Notifications from 'expo-notifications';
Notifications.scheduleNotificationAsync({
content: {
title: "You've got mail! 📬",
body: 'Here is the notification body',
},
trigger: { seconds: 2 },
});
9. Optimize API Calls and Caching
Making too many API requests can reduce performance, especially when dealing with large data sets. Use caching libraries like react-query
or Expo’s FileSystem
API to cache API results and improve performance.
Example with react-query
:
import { useQuery } from 'react-query';
const fetchPosts = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
return res.json();
};
const MyComponent = () => {
const { data, error, isLoading } = useQuery('posts', fetchPosts);
if (isLoading) return <Text>Loading...</Text>;
if (error) return <Text>Error loading data</Text>;
return (
<FlatList
data={data}
renderItem={({ item }) => <Text>{item.title}</Text>}
keyExtractor={(item) => item.id.toString()}
/>
);
};
10. Profile Performance Using Expo Tools
Expo provides some built-in profiling tools to analyze app performance:
- React DevTools: Allows you to inspect the component tree and detect unnecessary renders.
- Expo Performance Monitor: Use the performance monitor built into Expo to check frame rates and memory usage.
Conclusion
Optimizing performance in Expo apps is crucial for creating a seamless user experience. By using optimized images, minimizing re-renders, managing state efficiently, and taking advantage of tools like Hermes and react-native-reanimated
, you can ensure your Expo app runs smoothly. Keep experimenting with these techniques and tools to maintain a fast and responsive mobile app.
Stay Connected
Thank you for following along with this React Native (Expo) beginner’s guide! If you'd like to stay updated with more content or connect with me, feel free to follow me on:
- LinkedIn: Vincent Odukwe
- GitHub: Vrinch
- X (formerly Twitter): @VrinchOfficial
Happy coding, and I look forward to connecting with you!
Posted on October 10, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.