Introduction
Getting Started
- QuickStart
Patterns
- Languages
- Supported Languages
- Python
- Java
- JavaScript
- TypeScript
- Node.js
- React
- Fastify
- Next.js
- Terraform
- C#
- C++
- C
- Go
- Rust
- Swift
- React Native
- Spring Boot
- Kotlin
- Flutter
- Ruby
- PHP
- Scala
- Perl
- R
- Dart
- Elixir
- Erlang
- Haskell
- Lua
- Julia
- Clojure
- Groovy
- Fortran
- COBOL
- Pascal
- Assembly
- Bash
- PowerShell
- SQL
- PL/SQL
- T-SQL
- MATLAB
- Objective-C
- VBA
- ABAP
- Apex
- Apache Camel
- Crystal
- D
- Delphi
- Elm
- F#
- Hack
- Lisp
- OCaml
- Prolog
- Racket
- Scheme
- Solidity
- Verilog
- VHDL
- Zig
- MongoDB
- ClickHouse
- MySQL
- GraphQL
- Redis
- Cassandra
- Elasticsearch
- Security
- Performance
Integrations
- Code Repositories
- Team Messengers
- Ticketing
Enterprise
React Native is an open-source framework for building native mobile applications using JavaScript and React. It allows developers to use the same codebase for iOS and Android platforms.
React Native, while powerful for cross-platform mobile development, has several common anti-patterns that can lead to performance issues, maintenance problems, and poor user experience. Here are the most important anti-patterns to avoid when writing React Native code.
// Anti-pattern: Inefficient list rendering
function ProductList({ products }) {
return (
<ScrollView>
{products.map(product => (
<ProductItem key={product.id} product={product} />
))}
</ScrollView>
);
}
// Better approach: Use FlatList
function ProductList({ products }) {
return (
<FlatList
data={products}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => <ProductItem product={item} />}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
/>
);
}
Using ScrollView
with map
for long lists leads to performance issues as it renders all items at once. Use FlatList
or SectionList
for efficient list rendering with windowing and recycling.
// Anti-pattern: Regular component for static content
function ProfileHeader({ user }) {
return (
<View style={styles.header}>
<Image source={{ uri: user.avatar }} style={styles.avatar} />
<Text style={styles.name}>{user.name}</Text>
<Text style={styles.bio}>{user.bio}</Text>
</View>
);
}
// Better approach: Use memo for functional components
const ProfileHeader = React.memo(function ProfileHeader({ user }) {
return (
<View style={styles.header}>
<Image source={{ uri: user.avatar }} style={styles.avatar} />
<Text style={styles.name}>{user.name}</Text>
<Text style={styles.bio}>{user.bio}</Text>
</View>
);
});
// Or use PureComponent for class components
class ProfileHeader extends React.PureComponent {
render() {
const { user } = this.props;
return (
<View style={styles.header}>
<Image source={{ uri: user.avatar }} style={styles.avatar} />
<Text style={styles.name}>{user.name}</Text>
<Text style={styles.bio}>{user.bio}</Text>
</View>
);
}
}
Regular components re-render even when props haven’t changed. Use React.memo
for functional components or PureComponent
for class components to prevent unnecessary re-renders.
// Anti-pattern: Layout thrashing
function AnimatedComponent() {
const [width, setWidth] = useState(100);
useEffect(() => {
// This causes multiple layout recalculations
setWidth(100);
setWidth(150);
setWidth(200);
}, []);
return (
<Animated.View style={{ width }}>
<Text>Content</Text>
</Animated.View>
);
}
// Better approach: Batch layout changes
function AnimatedComponent() {
const width = useRef(new Animated.Value(100)).current;
useEffect(() => {
// This batches the animation
Animated.timing(width, {
toValue: 200,
duration: 300,
useNativeDriver: false,
}).start();
}, []);
return (
<Animated.View style={{ width }}>
<Text>Content</Text>
</Animated.View>
);
}
Multiple state updates that affect layout cause performance issues. Batch layout changes and use the Animated API with useNativeDriver
when possible.
// Anti-pattern: Recreating functions on each render
function ProductItem({ product, onAddToCart }) {
// This function is recreated on every render
const handlePress = () => {
onAddToCart(product.id);
};
return (
<TouchableOpacity onPress={handlePress}>
<Text>{product.name}</Text>
</TouchableOpacity>
);
}
// Better approach: Use useCallback
function ProductItem({ product, onAddToCart }) {
// This function is memoized
const handlePress = useCallback(() => {
onAddToCart(product.id);
}, [product.id, onAddToCart]);
return (
<TouchableOpacity onPress={handlePress}>
<Text>{product.name}</Text>
</TouchableOpacity>
);
}
Creating new function instances on every render can cause unnecessary re-renders in child components. Use useCallback
to memoize functions.
// Anti-pattern: Inline styles
function UserCard({ user }) {
return (
<View style={{
padding: 10,
margin: 5,
backgroundColor: '#fff',
borderRadius: 5,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
elevation: 2
}}>
<Text style={{ fontSize: 16, fontWeight: 'bold' }}>{user.name}</Text>
<Text style={{ color: '#666' }}>{user.email}</Text>
</View>
);
}
// Better approach: Use StyleSheet
const styles = StyleSheet.create({
card: {
padding: 10,
margin: 5,
backgroundColor: '#fff',
borderRadius: 5,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
elevation: 2
},
name: {
fontSize: 16,
fontWeight: 'bold'
},
email: {
color: '#666'
}
});
function UserCard({ user }) {
return (
<View style={styles.card}>
<Text style={styles.name}>{user.name}</Text>
<Text style={styles.email}>{user.email}</Text>
</View>
);
}
Inline styles are recreated on every render and can’t be optimized by React Native. Use StyleSheet.create()
to define styles outside the component.
// Anti-pattern: Not optimizing images
function ProfileScreen({ user }) {
return (
<View>
<Image
source={{ uri: user.highResAvatar }}
style={{ width: 50, height: 50 }}
/>
</View>
);
}
// Better approach: Optimize images
function ProfileScreen({ user }) {
return (
<View>
<Image
source={{ uri: `${user.avatar}?width=100&height=100` }}
style={{ width: 50, height: 50 }}
resizeMode="cover"
fadeDuration={300}
/>
</View>
);
}
Loading full-size images for small UI elements wastes bandwidth and memory. Request appropriately sized images and use proper resizeMode
.
// Anti-pattern: Creating navigator inside component
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
// Better approach: Separate navigation configuration
const Stack = createStackNavigator();
function AppNavigator() {
return (
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
}}
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={({ route }) => ({ title: route.params?.title || 'Home' })}
/>
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
);
}
Poor navigation structure leads to maintainability issues. Separate navigation configuration from components and use proper screen options.
// Anti-pattern: Platform-specific code scattered throughout
function Button({ title, onPress }) {
return (
<TouchableOpacity
onPress={onPress}
style={{
padding: 10,
backgroundColor: Platform.OS === 'ios' ? '#007AFF' : '#2196F3',
borderRadius: Platform.OS === 'ios' ? 10 : 4,
}}
>
<Text style={{
color: 'white',
fontWeight: Platform.OS === 'ios' ? '600' : 'bold',
}}>
{title}
</Text>
</TouchableOpacity>
);
}
// Better approach: Use Platform.select or separate files
const styles = StyleSheet.create({
button: {
padding: 10,
...Platform.select({
ios: {
backgroundColor: '#007AFF',
borderRadius: 10,
},
android: {
backgroundColor: '#2196F3',
borderRadius: 4,
},
}),
},
buttonText: {
color: 'white',
...Platform.select({
ios: { fontWeight: '600' },
android: { fontWeight: 'bold' },
}),
},
});
function Button({ title, onPress }) {
return (
<TouchableOpacity onPress={onPress} style={styles.button}>
<Text style={styles.buttonText}>{title}</Text>
</TouchableOpacity>
);
}
// Or use platform-specific files:
// Button.ios.js and Button.android.js
Scattering platform-specific code makes components hard to maintain. Use Platform.select()
or platform-specific files (e.g., Component.ios.js
and Component.android.js
).
// Anti-pattern: Prop drilling for global state
function App() {
const [user, setUser] = useState(null);
const [cart, setCart] = useState([]);
return (
<View>
<Header user={user} cart={cart} />
<Main user={user} cart={cart} setCart={setCart} />
<Footer user={user} />
</View>
);
}
// Better approach: Use Context API or Redux
const UserContext = createContext();
const CartContext = createContext();
function App() {
const [user, setUser] = useState(null);
const [cart, setCart] = useState([]);
return (
<UserContext.Provider value={{ user, setUser }}>
<CartContext.Provider value={{ cart, setCart }}>
<View>
<Header />
<Main />
<Footer />
</View>
</CartContext.Provider>
</UserContext.Provider>
);
}
function Header() {
const { user } = useContext(UserContext);
const { cart } = useContext(CartContext);
return <View>{/* Use user and cart */}</View>;
}
Prop drilling makes components tightly coupled and hard to maintain. Use Context API for simpler apps or Redux/MobX for complex state management.
// Anti-pattern: Not handling permissions properly
function CameraScreen() {
const takePicture = async () => {
try {
const photo = await camera.takePictureAsync();
// Process photo
} catch (error) {
console.error('Failed to take picture:', error);
}
};
return (
<View>
<Camera ref={ref => (camera = ref)} />
<Button title="Take Picture" onPress={takePicture} />
</View>
);
}
// Better approach: Check and request permissions
function CameraScreen() {
const [hasPermission, setHasPermission] = useState(null);
const [camera, setCamera] = useState(null);
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const takePicture = async () => {
if (camera) {
try {
const photo = await camera.takePictureAsync();
// Process photo
} catch (error) {
console.error('Failed to take picture:', error);
}
}
};
if (hasPermission === null) {
return <View><Text>Requesting camera permission...</Text></View>;
}
if (hasPermission === false) {
return <View><Text>No access to camera</Text></View>;
}
return (
<View>
<Camera ref={ref => setCamera(ref)} />
<Button title="Take Picture" onPress={takePicture} />
</View>
);
}
Not handling permissions properly leads to crashes and poor user experience. Always check and request permissions before using device features.
// Anti-pattern: No error handling
function App() {
return (
<View>
<Header />
<UserProfile /> {/* If this crashes, the whole app crashes */}
<Footer />
</View>
);
}
// Better approach: Use error boundaries
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Log error to error reporting service
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Something went wrong</Text>
<Button
title="Try Again"
onPress={() => this.setState({ hasError: false })}
/>
</View>
);
}
return this.props.children;
}
}
function App() {
return (
<View>
<Header />
<ErrorBoundary>
<UserProfile />
</ErrorBoundary>
<Footer />
</View>
);
}
Without error boundaries, a single component crash can bring down the entire app. Use error boundaries to gracefully handle component errors.
// Anti-pattern: Not considering accessibility
function LoginButton({ onPress }) {
return (
<TouchableOpacity onPress={onPress}>
<Text>Log In</Text>
</TouchableOpacity>
);
}
// Better approach: Include accessibility props
function LoginButton({ onPress }) {
return (
<TouchableOpacity
onPress={onPress}
accessible={true}
accessibilityLabel="Log in to your account"
accessibilityHint="Navigates to the login screen"
accessibilityRole="button"
>
<Text>Log In</Text>
</TouchableOpacity>
);
}
Ignoring accessibility makes your app unusable for many users. Include accessibility props like accessible
, accessibilityLabel
, and accessibilityHint
.
// Anti-pattern: Heavy operations in App component
function App() {
// Heavy initialization during app launch
const data = processLargeData();
initializeAllServices();
return (
<NavigationContainer>
<AppNavigator initialData={data} />
</NavigationContainer>
);
}
// Better approach: Defer non-critical operations
function App() {
const [isReady, setIsReady] = useState(false);
const [initialData, setInitialData] = useState(null);
useEffect(() => {
async function prepare() {
try {
// Only initialize critical services synchronously
initializeCriticalServices();
// Defer non-critical operations
setTimeout(() => {
initializeAnalytics();
initializeNonCriticalServices();
}, 1000);
// Load initial data asynchronously
const data = await loadInitialDataAsync();
setInitialData(data);
} catch (e) {
console.warn(e);
} finally {
setIsReady(true);
}
}
prepare();
}, []);
if (!isReady) {
return <AppLoading />;
}
return (
<NavigationContainer>
<AppNavigator initialData={initialData} />
</NavigationContainer>
);
}
Heavy operations during app launch increase startup time. Defer non-critical operations and use splash screens with AppLoading
component.
// In android/app/build.gradle
// Anti-pattern: Not enabling Hermes
project.ext.react = [
enableHermes: false // Not using Hermes engine
]
// Better approach: Enable Hermes
project.ext.react = [
enableHermes: true // Using Hermes engine for better performance
]
Not using Hermes (JavaScript engine optimized for React Native) can result in slower startup times and higher memory usage. Enable Hermes for better performance, especially on Android.