React Native is a framework developed by Meta (formerly Facebook) for building mobile applications using JavaScript and React. It allows developers to create cross-platform apps that work on iOS and Android using a single codebase.
React (or React.js) is a JavaScript library used for building web applications with a component-based architecture. It focuses on building interactive user interfaces (UIs) efficiently.
Feature | React (React.js) | React Native |
---|---|---|
Platform | Web applications | Mobile applications (iOS, Android) |
Rendering | Uses the DOM (Document Object Model) | Uses Native Components (e.g., <View> instead of <div> ) |
Styling | Uses CSS, SCSS, styled-components | Uses StyleSheet API (similar to inline styles in JS) |
Navigation | Uses React Router for routing | Uses React Navigation or Native Navigation |
Performance | Renders using the browser engine | Uses native components, making it faster than web apps |
APIs & Libraries | Uses browser APIs (e.g., fetch, localStorage) | Has access to mobile-specific APIs (e.g., camera, geolocation, push notifications) |
Compilation | Runs in the browser without additional compilation | Uses a bridge to convert JavaScript into native code |
You can create a new React Native project using Expo CLI (easier setup) or React Native CLI (more control). Here’s how:
Expo is a framework that simplifies React Native development by handling configurations.
Make sure you have Node.js installed. You can check by running:
node -v
npm -v
If not installed, download it from nodejs.org.
Run the following command to install Expo CLI globally:
npm install -g expo-cli
Run:
npx create-expo-app MyNewApp
Replace MyNewApp
with your project name.
cd MyNewApp
npm start
or
expo start
This will open the Expo Developer Tools in your browser. You can scan the QR code using the Expo Go app on your phone to run the app.
If you need more control (e.g., native modules, deeper customization), use React Native CLI.
brew install watchman
You don't need to install it globally. Just use:
npx react-native init MyNewApp
cd MyNewApp
For Android:
npx react-native run-android
For iOS (Mac + Xcode required):
npx react-native run-ios
Note: You need to have Android Studio (for Android) or Xcode (for iOS) set up properly.
Feature | React Native CLI | Expo |
---|---|---|
Setup Complexity | More complex (requires installing Xcode, Android Studio) | Easier (no need for native dependencies) |
Native Code Access | Full access to native code (Objective-C, Swift, Java, Kotlin) | Limited access (some native modules not supported) |
App Size | Smaller (can optimize native dependencies) | Larger (includes extra dependencies for ease of use) |
Performance | Faster (optimized for production) | Slightly slower (due to extra libraries) |
Third-Party Libraries | Supports all third-party native modules | Limited support for some native libraries |
OTA (Over-the-Air) Updates | Manual (requires app store updates) | Built-in with Expo Updates |
Ejecting | Not needed (full control from the start) | Requires Ejecting to use custom native code |
Best for | Advanced users, production apps with custom native modules | Beginners, rapid prototyping, simple apps |
* You need full control over native code.
* You are integrating third-party native libraries that Expo doesn’t support.
* You need optimized performance for production apps.
Example : Apps with complex native integrations like custom video processing, Bluetooth, background services, or AR/VR.
* You want a quick and easy setup.
* You don’t need deep native code modifications.
* You need over-the-air updates without app store approvals.
Example : Prototyping, MVPs, internal tools, or small to medium-sized apps with no custom native code requirements.
You can eject from Expo using:
npx expo prebuild
This converts your Expo project into a standard React Native CLI project.
React Native does not use traditional CSS like web development. Instead, it uses a JavaScript-based StyleSheet API similar to inline styles.
You can apply styles directly to components using JavaScript objects.
import React from 'react';
import { Text, View } from 'react-native';
const App = () => {
return (
<View>
<Text style={{ color: 'blue', fontSize: 20 }}>Hello, React Native!</Text>
</View>
);
};
export default App;
* Pros: Quick and easy for small styles.
* Cons: Hard to maintain for larger projects.
StyleSheet
API (Recommended)The StyleSheet.create
method helps optimize styles by preventing unnecessary re-renders.
import React from 'react';
import { Text, View, StyleSheet } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>Styled with StyleSheet!</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f0f0f0',
},
text: {
fontSize: 20,
color: 'blue',
fontWeight: 'bold',
},
});
export default App;
* Pros: Improves performance, easier to maintain.
* Cons: Still lacks CSS features like media queries.
Styled-components allow writing styles similarly to CSS but in JavaScript.
npm install styled-components
or
yarn add styled-components
import React from 'react';
import { Text, View } from 'react-native';
import styled from 'styled-components/native';
const Container = styled(View)`
flex: 1;
justify-content: center;
align-items: center;
background-color: #f0f0f0;
`;
const StyledText = styled(Text)`
font-size: 20px;
color: blue;
font-weight: bold;
`;
const App = () => {
return (
<Container>
<StyledText>Styled with Styled Components!</StyledText>
</Container>
);
};
export default App;
* Pros: Looks like CSS, easy to manage styles.
* Cons: Can affect performance slightly.
Platform
API)Use Platform
API to style differently based on iOS or Android.
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
text: {
fontSize: 20,
color: Platform.OS === 'ios' ? 'blue' : 'green',
},
});
* Pros: Customize styles for each platform.
* Cons: Adds complexity.
For global styles, you can use:
react-native-paper
or tailwind-rn
Example using styled-components
theme:
import { ThemeProvider } from 'styled-components/native';
const theme = {
colors: {
primary: 'blue',
background: '#f0f0f0',
},
};
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>;
Method | Best For |
---|---|
Inline Styles | Quick styling, small components |
StyleSheet API | Most recommended, optimized |
Styled Components | Better readability, theme-based apps |
Platform API | Platform-specific styles |
Global Themes | Large apps with consistent design |
React Native uses Flexbox for layout, just like CSS, but with some differences. Flexbox helps create responsive UIs that work on different screen sizes.
Here are the key Flexbox properties in React Native:
Property | Description |
---|---|
flex |
Defines how much space an item should take. |
flexDirection |
Sets the direction of children (row or column ). |
justifyContent |
Aligns children along the main axis. |
alignItems |
Aligns children along the cross axis. |
alignSelf |
Aligns a single item on the cross axis. |
flexWrap |
Wraps items if they exceed the container size. |
gap , rowGap , columnGap |
Adds spacing between elements (React Native 0.71+). |
flex
WorksThe flex
property determines how much space an item takes.
<View style={{ flex: 1, backgroundColor: 'lightblue' }} />
<View style={{ flex: 2, backgroundColor: 'lightgreen' }} />
* The second view takes twice the space of the first.
flexDirection
: Row vs. ColumnBy default, React Native uses column
, unlike CSS, which defaults to row
.
<View style={{ flexDirection: 'row' }}>
<View style={{ width: 50, height: 50, backgroundColor: 'red' }} />
<View style={{ width: 50, height: 50, backgroundColor: 'blue' }} />
</View>
* Options:
row
→ Horizontal layoutcolumn
(default) → Vertical layoutrow-reverse
→ Reverse horizontalcolumn-reverse
→ Reverse verticaljustifyContent
: Align Items on the Main AxisUsed to position items along the main axis (horizontal for row
, vertical for column
).
<View style={{ flex: 1, justifyContent: 'center' }}>
<Text>Centered</Text>
</View>
* Values :
flex-start
(default) → Items start at the beginningcenter
→ Items centeredflex-end
→ Items at the endspace-between
→ Space between itemsspace-around
→ Space around itemsspace-evenly
→ Equal spacingalignItems
: Align Items on the Cross AxisControls alignment along the cross axis (opposite of justifyContent
).
<View style={{ flex: 1, alignItems: 'center' }}>
<Text>Aligned Center</Text>
</View>
* Values :
flex-start
→ Items at startcenter
→ Items in the middleflex-end
→ Items at endstretch
(default) → Items stretch to fill the containeralignSelf
: Align a Single ItemOverrides alignItems
for a specific child.
<Text style={{ alignSelf: 'flex-end' }}>I move alone</Text>
flexWrap
: Wrapping ItemsBy default, items stay in a single row/column. Use flexWrap
to wrap them.
<View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
<View style={{ width: 100, height: 50, backgroundColor: 'red' }} />
<View style={{ width: 100, height: 50, backgroundColor: 'blue' }} />
<View style={{ width: 100, height: 50, backgroundColor: 'green' }} />
</View>
* Values :
nowrap
(default) → No wrappingwrap
→ Items wrap to the next linegap
, rowGap
, columnGap
(React Native 0.71+)These properties add spacing between items.
<View style={{ flexDirection: 'row', gap: 10 }}>
<View style={{ width: 50, height: 50, backgroundColor: 'red' }} />
<View style={{ width: 50, height: 50, backgroundColor: 'blue' }} />
</View>
* rowGap
: Controls gap between rows.
* columnGap
: Controls gap between columns.
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<View style={styles.box}><Text>1</Text></View>
<View style={styles.box}><Text>2</Text></View>
<View style={styles.box}><Text>3</Text></View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#f0f0f0',
},
box: {
width: 100,
height: 100,
backgroundColor: 'skyblue',
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
* flex: 1
→ Takes up the full screen.
* flexDirection: 'row'
→ Places items in a row.
* justifyContent: 'space-between'
→ Evenly spaces items.
* alignItems: 'center'
→ Centers items vertically.
Use Case | Recommended Properties |
---|---|
Stack items horizontally | flexDirection: 'row' |
Center an item inside a container | justifyContent: 'center' , alignItems: 'center' |
Distribute items evenly | justifyContent: 'space-between' or space-around |
Allow items to wrap | flexWrap: 'wrap' |
Apply spacing between elements | gap , rowGap , columnGap |
Performance optimization is crucial for a smooth user experience in React Native apps. Here are the best techniques:
useMemo
and useCallback
to Prevent Unnecessary RendersReact components re-render when props or state change. Use useMemo
and useCallback
to optimize performance.
useMemo
: Caches expensive calculations.useCallback
: Caches function instances.import React, { useState, useMemo, useCallback } from 'react';
import { View, Text, Button } from 'react-native';
const ExpensiveComponent = ({ count }) => {
const computedValue = useMemo(() => {
console.log('Recalculating...');
return count * 2;
}, [count]);
return <Text>Computed Value: {computedValue}</Text>;
};
const App = () => {
const [count, setCount] = useState(0);
const increment = useCallback(() => setCount(count + 1), [count]);
return (
<View>
<ExpensiveComponent count={count} />
<Button title="Increment" onPress={increment} />
</View>
);
};
export default App;
* Reduces re-renders, improving performance.
React.memo
to Avoid Re-Rendering Unchanged ComponentsReact re-renders child components even if their props haven’t changed. Use React.memo
to prevent this.
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
const MemoizedText = React.memo(({ text }) => {
console.log('Rendering MemoizedText');
return <Text>{text}</Text>;
});
const App = () => {
const [count, setCount] = useState(0);
return (
<View>
<MemoizedText text="This component won't re-render" />
<Text>Count: {count}</Text>
<Button title="Increment" onPress={() => setCount(count + 1)} />
</View>
);
};
export default App;
* Prevents unnecessary re-renders of static components.
When dealing with large lists, avoid using ScrollView
. Instead, use FlatList
, which efficiently renders only the visible items.
<ScrollView>
{data.map(item => (
<Text key={item.id}>{item.name}</Text>
))}
</ScrollView>
* ScrollView
loads all items into memory at once, causing slowdowns.
<FlatList
data={data}
keyExtractor={item => item.id}
renderItem={({ item }) => <Text>{item.name}</Text>}
initialNumToRender={10} // Render only 10 items initially
maxToRenderPerBatch={10} // Load items in batches
windowSize={5} // Keep only 5 screens worth of items in memory
/>
* FlatList only renders visible items, improving performance.
react-native-fast-image
)Large, unoptimized images can slow down your app.
react-native-fast-image
Install it:
npm install react-native-fast-image
Usage:
import FastImage from 'react-native-fast-image';
<FastImage
style={{ width: 200, height: 200 }}
source={{ uri: 'https://example.com/image.jpg' }}
resizeMode={FastImage.resizeMode.cover}
/>
InteractionManager
for Heavy TasksTo avoid blocking UI updates, defer heavy tasks using InteractionManager
.
import { InteractionManager } from 'react-native';
useEffect(() => {
const task = InteractionManager.runAfterInteractions(() => {
// Perform expensive operations here
});
return () => task.cancel();
}, []);
* Ensures animations and UI remain smooth.
setState
CallsEvery time you update state, React re-renders the component. Minimize unnecessary state updates.
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
setCount(count + 2);
};
* Causes multiple re-renders.
const increment = () => {
setCount(prev => prev + 2);
};
Hermes is a lightweight JavaScript engine optimized for React Native.
Edit android/app/build.gradle
:
enableHermes: true
Then rebuild the project:
cd android && ./gradlew clean
cd .. && npx react-native run-android
* Improves startup time and reduces memory usage.
react-native-reanimated
)Use react-native-reanimated
for smoother animations instead of Animated
.
react-native-reanimated
npm install react-native-reanimated
import Animated, { Easing } from 'react-native-reanimated';
const animation = new Animated.Value(0);
Animated.timing(animation, {
toValue: 1,
duration: 500,
easing: Easing.linear,
}).start();
redux-persist
If you use Redux, persist the state to avoid unnecessary API calls.
redux-persist
npm install redux-persist
import { persistStore, persistReducer } from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';
const persistConfig = { key: 'root', storage: AsyncStorage };
const persistedReducer = persistReducer(persistConfig, rootReducer);
* Reduces redundant API calls by caching state.
useNativeDriver
for AnimationsAnimations in React Native should offload work to the GPU.
Animated.timing(animatedValue, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
* Prevents UI thread lag.
Optimization Tip | Benefit |
---|---|
useMemo & useCallback |
Prevents unnecessary re-renders |
React.memo |
Caches components |
Use FlatList instead of ScrollView |
Improves list performance |
Use react-native-fast-image |
Faster image loading |
Use InteractionManager |
Defer heavy tasks |
Minimize setState calls |
Reduces unnecessary re-renders |
Enable Hermes (Android) | Speeds up execution |
Use react-native-reanimated |
Improves animation smoothness |
Persist Redux state | Avoids redundant API calls |
Use useNativeDriver |
Runs animations on the GPU |
import React, { useState } from 'react';
import { Text, TextInput, View } from 'react-native';
const PizzaTranslator = () => {
const [text, setText] = useState('');
return (
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Type here to translate!"
onChangeText={text => setText(text)}
defaultValue={text}
/>
<Text style={{padding: 10, fontSize: 42}}>
{text.split(' ').map((word) => word && '?').join(' ')}
</Text>
</View>
);
}
export default PizzaTranslator;?
import React, {Component} from 'react';
import {View, Text} from 'react-native';
class DefaultPropComponent extends Component {
render() {
return (
<View>
<Text>
{this.props.name}
</Text>
</View>
)
}
}
Demo.defaultProps = {
name: 'BOB'
}
export default DefaultPropComponent;?
In React Native, you import components using JavaScript's import
statement.
React Native provides core components like View
, Text
, Button
, etc.
import { View, Text, Button } from 'react-native';
* Example Usage :
import React from 'react';
import { View, Text, Button } from 'react-native';
const App = () => {
return (
<View>
<Text>Hello, React Native!</Text>
<Button title="Click Me" onPress={() => alert('Button Pressed!')} />
</View>
);
};
export default App;
For better code organization, you can create reusable custom components in separate files.
MyComponent.js
)import React from 'react';
import { Text } from 'react-native';
const MyComponent = () => {
return <Text style={{ fontSize: 20 }}>Hello from MyComponent!</Text>;
};
export default MyComponent;
import React from 'react';
import { View } from 'react-native';
import MyComponent from './MyComponent'; // Importing the custom component
const App = () => {
return (
<View>
<MyComponent />
</View>
);
};
export default App;
as
)You can rename imported components using as
.
import { Text as RNText, View as RNView } from 'react-native';
<RNView>
<RNText>Hello with alias!</RNText>
</RNView>
{}
)// MyComponent.js
export default function MyComponent() {
return <Text>Hello!</Text>;
}
// Import it without curly braces
import MyComponent from './MyComponent';
{}
)// MyComponents.js
export const Header = () => <Text>Header</Text>;
export const Footer = () => <Text>Footer</Text>;
// Import specific components
import { Header, Footer } from './MyComponents';
If you install a library like react-native-vector-icons
:
npm install react-native-vector-icons
You import components like this:
import Icon from 'react-native-vector-icons/FontAwesome';
<Icon name="home" size={30} color="blue" />
*
)You can import all exports from a module as a single object.
import * as MyComponents from './MyComponents';
<MyComponents.Header />
<MyComponents.Footer />
Import Type | Syntax |
---|---|
Core Component | import { View, Text } from 'react-native'; |
Custom Component | import MyComponent from './MyComponent'; |
Named Exports | import { Header, Footer } from './MyComponents'; |
Import with Alias | import { Text as RNText } from 'react-native'; |
Import All (* ) |
import * as MyComponents from './MyComponents'; |
In React Native (and React in general), components are the building blocks of your user interface. Historically, there were two main ways to define components: class components and functional components. However, with the introduction of React Hooks, functional components have become the dominant and recommended approach. Here's a breakdown:
Class Components:
React.Component
.render()
method that returns the JSX (JavaScript XML) that defines the component's UI.this.state
.componentDidMount
, componentDidUpdate
, componentWillUnmount
), which allow you to perform actions at specific points in the component's lifecycle.import React, { Component } from 'react';
import { Text } from 'react-native';
class MyClassComponent extends Component {
render() {
return <Text>Hello from a class component!</Text>;
}
}
export default MyClassComponent;
Functional Components:
useState
and perform side effects using useEffect
, effectively replicating the capabilities of class components.import React from 'react';
import { Text } from 'react-native';
const MyFunctionalComponent = () => {
return <Text>Hello from a functional component!</Text>;
};
export default MyFunctionalComponent;
Key Differences and the Impact of Hooks :
useState
hook.useEffect
hook.<View>
, <Text>
, <Image>
, <ScrollView>
, <TextInput>
REACT NATIVE UI COMPONENT | ANDROID VIEW | IOS VIEW | WEB ANALOG | DESCRIPTION |
---|---|---|---|---|
<View> |
<ViewGroup> |
<UIView> |
A non-scrolling <div> |
A container that supports layout with flexbox style, some touch handling, and accessibility controls. |
<Text> |
<TextView> |
<UITextView> |
<p> |
Displays, styles, and nests strings of text and even handles touch events. |
<Image> |
<ImageView> |
<UIImageView> |
<img> |
Displays different types of images |
<ScrollView> |
<ScrollView> |
<UIScrollView> |
<div> |
A generic scrolling container that can contain multiple components and views. |
<TextInput> |
<EditText> |
<UITextField> |
<input type="text"> |
Allows the user to enter text |
DO USE ASYNC STORAGE WHEN.. | DON'T USE ASYNC STORAGE FOR.. |
---|---|
Persisting non-sensitive data across app runs | Token storage |
Persisting Redux state | Secrets |
Persisting GraphQL state | |
Storing global app-wide variables |
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
const Stack = createStackNavigator();
const MyStack = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome' }}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};?
import React, { Component } from 'react';
import { AppRegistry, FlatList,
StyleSheet, Text, View,Alert } from 'react-native';
export default class FlatListBasics extends Component {
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#000",
}}
/>
);
};
//handling onPress action
getListViewItem = (item) => {
Alert.alert(item.key);
}
render() {
return (
<View style={styles.container}>
<FlatList
data={[
{key: 'Android'},{key: 'iOS'}, {key: 'Java'},{key: 'Swift'},
{key: 'Php'},{key: 'Hadoop'},{key: 'Sap'},
]}
renderItem={({item}) =>
<Text style={styles.item}
onPress={this.getListViewItem.bind(this, item)}>{item.key}</Text>}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
);
}
}
AppRegistry.registerComponent('AwesomeProject', () => FlatListBasics);?
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, TouchableHighlight, TouchableOpacity, TouchableNativeFeedback, TouchableWithoutFeedback, View } from 'react-native';
export default class Touchables extends Component {
_onPressButton() {
alert('You tapped the button!') }
_onLongPressButton() {
alert('You long-pressed the button!')
}
render() {
return (
<View style={styles.container}>
<TouchableHighlight onPress={this._onPressButton} underlayColor="white">
<View style={styles.button}>
<Text style={styles.buttonText}>TouchableHighlight</Text>
</View>
</TouchableHighlight>
);}
}?
import { Platform, Stylesheet } from 'react-native';
const styles = Stylesheet.create({
height: Platform.OS === 'IOS' ? 200 : 400
})?
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
...Platform.select({
ios: {
backgroundColor: 'red',
},
android: {
backgroundColor: 'green',
},
default: {
// other platforms, web for example
backgroundColor: 'blue',
}, }),
},
});?
export default class MyListComponent extends Component {
constructor() {
super();
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(['Android','iOS', 'Java','Php', 'Hadoop', 'Sap', 'Python','Ajax', 'C++']),
};
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={
(rowData) =>
<Text style={{fontSize: 30}}>{rowData}</Text>} />
); }
}?
In React Native, you can make API calls using fetch
, Axios, or libraries like React Query for better state management.
fetch
(Built-in JavaScript Method)The fetch
API is the easiest way to make network requests in React Native.
import React, { useEffect, useState } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
const App = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(json => {
setData(json);
setLoading(false);
})
.catch(error => console.error('Error fetching data:', error));
}, []);
return (
<View>
{loading ? (
<ActivityIndicator size="large" color="blue" />
) : (
<Text>{data?.title}</Text>
)}
</View>
);
};
export default App;
* Handles API calls, updates state, and displays a loading indicator.
fetch
with async/await
For better readability, use async/await
.
const fetchData = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const json = await response.json();
setData(json);
} catch (error) {
console.error('Error:', error);
}
};
* Cleaner syntax with error handling.
Send data to an API using fetch
with POST
method.
const sendData = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'React Native', body: 'Hello API!', userId: 1 }),
});
const json = await response.json();
console.log('Response:', json);
} catch (error) {
console.error('Error:', error);
}
};
* Sends JSON data to an API.
fetch
)Axios is a popular library for API calls because it supports automatic JSON conversion and better error handling.
npm install axios
import axios from 'axios';
const fetchData = async () => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
console.log(response.data);
} catch (error) {
console.error('Error:', error);
}
};
* Easier error handling and cleaner syntax.
React Query helps with caching and automatic refetching.
npm install @tanstack/react-query
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import { View, Text, ActivityIndicator } from 'react-native';
import axios from 'axios';
const queryClient = new QueryClient();
const fetchPost = async () => {
const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
return data;
};
const Post = () => {
const { data, isLoading } = useQuery(['post'], fetchPost);
if (isLoading) return <ActivityIndicator size="large" color="blue" />;
return <Text>{data.title}</Text>;
};
const App = () => (
<QueryClientProvider client={queryClient}>
<Post />
</QueryClientProvider>
);
export default App;
* Caches responses and automatically refetches data when needed.
Always include error handling when making API calls.
const fetchData = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
if (!response.ok) throw new Error('Network response was not ok');
const json = await response.json();
setData(json);
} catch (error) {
console.error('API Error:', error.message);
}
};
* Prevents crashes by catching API errors.
* Use caching (e.g., React Query, Redux Toolkit Query).
* Debounce API calls to avoid excessive requests.
* Use background fetch for real-time updates.
* Paginate large datasets (use ?page=1&limit=10
).
In React Native, AsyncStorage, SecureStore, and Redux Persist are used for storing and persisting data. Each has different use cases and security levels.
AsyncStorage is a key-value storage system that allows you to store data persistently on the device.
npm install @react-native-async-storage/async-storage
import AsyncStorage from '@react-native-async-storage/async-storage';
// Save data
const saveData = async () => {
await AsyncStorage.setItem('username', 'JohnDoe');
};
// Retrieve data
const getData = async () => {
const value = await AsyncStorage.getItem('username');
console.log(value);
};
// Remove data
const removeData = async () => {
await AsyncStorage.removeItem('username');
};
* Easy to use but not recommended for storing sensitive data.
SecureStore (from expo-secure-store
) is a secure storage solution that encrypts data, making it ideal for storing sensitive information like tokens, passwords, and user credentials.
expo install expo-secure-store
import * as SecureStore from 'expo-secure-store';
// Save data securely
const saveSecureData = async () => {
await SecureStore.setItemAsync('token', 'secure-token-123');
};
// Retrieve data
const getSecureData = async () => {
const value = await SecureStore.getItemAsync('token');
console.log(value);
};
// Remove data
const removeSecureData = async () => {
await SecureStore.deleteItemAsync('token');
};
* Best choice for storing sensitive user information.
Redux Persist is used to persist Redux state across app restarts. It integrates with storage solutions like AsyncStorage.
npm install redux-persist @react-native-async-storage/async-storage
import { persistStore, persistReducer } from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createStore } from 'redux';
// Define a reducer
const reducer = (state = { user: null }, action) => {
switch (action.type) {
case 'SET_USER':
return { ...state, user: action.payload };
default:
return state;
}
};
// Configure Redux Persist
const persistConfig = { key: 'root', storage: AsyncStorage };
const persistedReducer = persistReducer(persistConfig, reducer);
const store = createStore(persistedReducer);
const persistor = persistStore(store);
* Great for persisting Redux state without losing data on app reload.
Feature | AsyncStorage | SecureStore | Redux Persist |
---|---|---|---|
Security | Plain text (not secure) | Encrypted storage | Uses AsyncStorage (not secure) |
Use Case | Caching, non-sensitive data | Sensitive data (passwords, tokens) | Persisting Redux state |
Works Offline? | Yes | Yes | Yes |
Performance | Slower for large data | Fast for small data | Optimized for Redux |
Storage Limit | Device-dependent | Small size | Limited by AsyncStorage |
Platform Support | Android & iOS | Android & iOS (Expo) | Android & iOS |
To distribute your React Native app, you need to generate an APK (Android) or IPA (iOS) file. Below is a step-by-step guide for both platforms.
You can generate an APK using either React Native CLI or Expo.
android/app/build.gradle
signingConfig signingConfigs.debug
To:
signingConfig signingConfigs.release
Run this command in the android
folder:
keytool -genkeypair -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
? This will create a my-release-key.keystore
file.
Move it to android/app/
.
Edit android/gradle.properties
and add:
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=your-password
MYAPP_RELEASE_KEY_PASSWORD=your-password
Run:
cd android
./gradlew assembleRelease
* Your APK is generated at:android/app/build/outputs/apk/release/app-release.apk
Expo makes it easier to generate an APK.
npm install -g expo-cli
eas build -p android --profile production
* Expo will generate the APK and provide a download link.
iOS apps require a Mac and an Apple Developer Account.
sudo gem install cocoapods
cd ios
pod install
ios/App.xcworkspace
in Xcode..ipa
file.npm install -g expo-cli
eas build -p ios --profile production
Expo will generate an .ipa
file and provide a link.
Continuous Integration and Continuous Deployment (CI/CD) automate building, testing, and deploying React Native apps. Below is a step-by-step guide to setting up CI/CD using GitHub Actions, Fastlane, and cloud services like EAS, Bitrise, or App Center.
You can set up CI/CD using:
CI/CD Tool | Platform | Best For |
---|---|---|
GitHub Actions | Free for GitHub projects | Custom pipelines |
Bitrise | Android & iOS | No need for macOS |
EAS (Expo) | Expo apps | Easiest setup |
App Center | Android & iOS | Microsoft ecosystem |
Fastlane | Android & iOS | Advanced automation |
GitHub Actions is a free way to automate builds and tests.
.github/workflows/ci.yml
FileInside your React Native project, create the following CI/CD workflow:
name: React Native CI/CD
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-android:
name: Build Android APK
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install Dependencies
run: npm install
- name: Build Android APK
run: cd android && ./gradlew assembleRelease
- name: Upload APK
uses: actions/upload-artifact@v3
with:
name: app-release.apk
path: android/app/build/outputs/apk/release/app-release.apk
build-ios:
name: Build iOS IPA
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Install Dependencies
run: npm install
- name: Install CocoaPods
run: cd ios && pod install
- name: Build iOS App
run: xcodebuild -workspace ios/App.xcworkspace -scheme App -sdk iphoneos -configuration Release archive -archivePath ios/build/App.xcarchive
- name: Upload IPA
uses: actions/upload-artifact@v3
with:
name: app-release.ipa
path: ios/build/App.xcarchive
* This workflow :
main
Fastlane helps deploy your app to Google Play Store and App Store.
npm install -g fastlane
cd android
fastlane init
Then, create android/fastlane/Fastfile
with:
platform :android do
desc "Build and upload APK to Play Store"
lane :deploy do
gradle(task: "assembleRelease")
upload_to_play_store(track: "production")
end
end
Run:
fastlane android deploy
cd ios
fastlane init
Modify ios/fastlane/Fastfile
:
platform :ios do
desc "Build and upload IPA to App Store"
lane :deploy do
gym(scheme: "App")
upload_to_app_store
end
end
Run:
fastlane ios deploy
* Fastlane automates signing, building, and uploading your app!
npm install -g eas-cli
eas login
eas build:configure
eas build -p android
eas submit -p android
* This uploads the app directly to the Play Store/App Store!
Bitrise is a cloud-based CI/CD tool for React Native.
* Great for teams that don’t have macOS!
Tool | Setup Time | Best For |
---|---|---|
GitHub Actions | Medium | Free, custom builds |
Fastlane | Medium | Play Store/App Store uploads |
EAS (Expo) | Easy | Expo apps |
Bitrise | Easy | No Mac needed |
App Center | Medium | Microsoft ecosystem |
Network security is the practice of protecting data during transmission between a mobile app and a server. It prevents data breaches, hacking, and man-in-the-middle (MITM) attacks.
* Encryption: Uses protocols like TLS (Transport Layer Security) to encrypt data.
* Authentication: Verifies user identity (e.g., JWT tokens, OAuth).
* SSL/TLS Certificates: Ensure the server is legitimate and secure.
* Firewalls & VPNs: Restrict unauthorized access.
SSL Pinning is an advanced security technique used to prevent MITM attacks by hardcoding the server’s SSL certificate inside the mobile app.
* Prevents attackers from intercepting HTTPS traffic
* Stops fake certificates from being accepted
* Ensures communication with the intended server
Hackers can intercept HTTPS traffic, fake a server’s certificate, and steal sensitive data (e.g., API keys, user credentials).
Even if an attacker tries to use a fake certificate, the app will reject it!
To implement SSL Pinning in a React Native app, use the react-native-ssl-pinning
library.
npm install react-native-ssl-pinning
or
yarn add react-native-ssl-pinning
import { fetch } from 'react-native-ssl-pinning';
fetch('https://api.example.com/data', {
method: 'GET',
headers: { Accept: 'application/json' },
sslPinning: {
certs: ['my-cert'], // Reference to pinned certificate
},
})
.then(response => response.json())
.then(data => console.log('Secure data:', data))
.catch(error => console.error('SSL Pinning failed:', error));
openssl s_client -connect api.example.com:443 -showcerts
.cer
file in your project.* App Updates Required – If the server updates its SSL certificate, you must release a new app update.
* More Maintenance – Requires managing certificate expiration.
* Device-Specific Bypasses – Advanced attackers can still bypass SSL pinning using rooted devices.
* Use TLS 1.2+ for secure data transmission.
* Implement SSL Pinning to prevent MITM attacks.
* Use OAuth, JWT, or API Keys for authentication.
* Never store sensitive data in AsyncStorage (use SecureStore for Expo).
* Use network monitoring tools like Charles Proxy for security testing.
Feature | SSL Pinning | General Network Security |
---|---|---|
Prevents MITM Attacks | Yes | Limited |
Requires Manual Updates | Yes | No |
Works with HTTPS? | Yes | Yes |
Easy to Implement? | Medium | Yes |