Native Modules and Components
Native Modules
Native Modules in React Native enable calling native code (Swift/Objective-C for iOS or Java/Kotlin for Android) when built-in components or APIs are insufficient. They extend functionality for specific needs.
Principle Analysis
- Bridge Mechanism: JavaScript communicates with the native layer via a bridge, passing instructions and data.
- Use Cases: Accessing device hardware (e.g., sensors, Bluetooth) or integrating third-party native libraries.
Creating a Native Module (Android Example)
Let’s create a module to retrieve device battery level.
- Step 1: Write Java Code
CreateBatteryModule.javainandroid/app/src/main/java/com/yourapp/:
package com.yourapp;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
import android.os.BatteryManager;
import android.content.Intent;
import android.content.Context;
public class BatteryModule extends ReactContextBaseJavaModule {
public BatteryModule(ReactApplicationContext context) {
super(context);
}
@Override
public String getName() {
return "BatteryModule"; // Module name used in JS
}
@ReactMethod
public void getBatteryLevel(Promise promise) {
Intent batteryIntent = getCurrentActivity().registerReceiver(null,
new android.content.IntentFilter(Intent.ACTION_BATTERY_CHANGED));
int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryLevel = level / (float) scale * 100; // Calculate percentage
promise.resolve(batteryLevel);
}
}
- Step 2: Register the Module
CreateBatteryPackage.java:
package com.yourapp;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Collections;
import java.util.List;
public class BatteryPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.singletonList(new BatteryModule(reactContext));
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Modify MainApplication.java to add the package:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.asList(
new MainReactPackage(),
new BatteryPackage() // Register custom package
);
}
- Step 3: Call from JavaScript
import { NativeModules } from 'react-native';
const { BatteryModule } = NativeModules;
const checkBattery = async () => {
try {
const level = await BatteryModule.getBatteryLevel();
console.log(`Battery Level: ${level}%`);
} catch (error) {
console.error(error);
}
};
checkBattery();
- Analysis:
@ReactMethodexposes methods to JS.Promisehandles asynchronous results.- JS accesses native methods via
NativeModules.
iOS Example
Similarly, iOS implements modules in Objective-C or Swift and registers them with RCTBridge.
Native Components
Native Components are custom native UI components, often used with Native Modules.
- Android Example: Custom button.
- Java Code:
public class CustomButtonManager extends SimpleViewManager<Button> {
public static final String REACT_CLASS = "CustomButton";
@Override
public String getName() {
return REACT_CLASS;
}
@Override
protected Button createViewInstance(ThemedReactContext reactContext) {
Button button = new Button(reactContext);
button.setText("Native Button");
return button;
}
}
- Register: Add to
createViewManagersinBatteryPackage.java:
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.singletonList(new CustomButtonManager());
}
- JS Usage:
import { requireNativeComponent } from 'react-native';
const CustomButton = requireNativeComponent('CustomButton');
const App = () => (
<View>
<CustomButton style={{ width: 200, height: 50 }} />
</View>
);
export default App;
- Analysis: Native Components render native UI directly, offering better performance and flexibility.
Layout System – Flexbox
Flexbox Basics
React Native uses Flexbox for layout, similar to Web CSS Flexbox but optimized for mobile.
Core Concepts
- Flex Container: Uses
<View>as a container withflexproperties. - Flex Items: Child elements controlled by container properties.
- Key Properties:
flexDirection: Arrangement direction (row,column).justifyContent: Main axis alignment (flex-start,center,space-between, etc.).alignItems: Cross-axis alignment (stretch,center, etc.).
Example: Centered Horizontal Layout
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const App = () => (
<View style={styles.container}>
<Text style={styles.text}>Hello, Flexbox!</Text>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1, // Fill parent container
flexDirection: 'row', // Horizontal arrangement
justifyContent: 'center', // Center on main axis
alignItems: 'center', // Center on cross axis
backgroundColor: '#f0f0f0'
},
text: {
fontSize: 20,
color: '#333'
}
});
export default App;
- Analysis:
flex: 1makes the container fill the screen.flexDirection: 'row'sets horizontal layout.justifyContentandalignItemscenter the text.
Example: List Layout
const App = () => (
<View style={styles.container}>
{[1, 2, 3].map(num => (
<View key={num} style={styles.item}>
<Text>Item {num}</Text>
</View>
))}
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'space-around',
padding: 10
},
item: {
height: 100,
backgroundColor: '#ddd',
marginVertical: 5,
justifyContent: 'center',
alignItems: 'center'
}
});
- Analysis:
flexDirection: 'column'arranges vertically.justifyContent: 'space-around'distributes evenly along the main axis.
Notes
- Units: React Native uses logical pixels, not
px. - Default Value:
flexDirectiondefaults tocolumn(unlike Web’srow).
Resource Management – Image and StyleSheet
Image Component
The Image component displays images, supporting local and network resources.
Local Images
- Storage Path:
src/assets/images/logo.png. - Usage:
import { Image, View } from 'react-native';
const App = () => (
<View>
<Image
source={require('../assets/images/logo.png')}
style={{ width: 100, height: 100 }}
/>
</View>
);
export default App;
Network Images
const App = () => (
<View>
<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={{ width: 200, height: 200 }}
resizeMode="cover" // 'contain', 'stretch', etc.
/>
</View>
);
- Properties:
resizeMode: Controls scaling (covercrops to fill,containshows fully).onLoad: Callback for image load completion.
Caching and Optimization
- Caching is enabled by default; control via
cacheproperty (Android requires additional configuration).
StyleSheet
StyleSheet is React Native’s style management tool, optimizing performance and readability.
Basic Usage
import { StyleSheet, View, Text } from 'react-native';
const App = () => (
<View style={styles.container}>
<Text style={styles.text}>Styled Text</Text>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
padding: 20
},
text: {
fontSize: 18,
color: '#007AFF',
fontWeight: 'bold'
}
});
export default App;
- Analysis:
StyleSheet.createcreates style objects, reducing runtime computation.- Uses camelCase (e.g.,
fontSizeinstead offont-size).
Dynamic Styles
const App = () => {
const [isActive, setIsActive] = React.useState(false);
return (
<View style={[styles.container, isActive && styles.active]}>
<Text onPress={() => setIsActive(!isActive)}>
Toggle: {isActive ? 'Active' : 'Inactive'}
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
active: {
backgroundColor: '#e0f7fa'
}
});
- Analysis:
- Merges styles using arrays.
isActive && styles.activeapplies styles conditionally.
Performance Benefits
- Inline Styles (e.g.,
<Text style={{ fontSize: 18 }}>) recompute on each render. - StyleSheet caches style IDs, improving render efficiency.
Data Requests and Caching
Fetch API Basics
Basic Request Example
// GET request example
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Fetch error:', error);
}
};
// POST request example
const postData = async () => {
try {
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
key1: 'value1',
key2: 'value2',
}),
});
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Fetch error:', error);
}
};
Request Timeout Handling
const fetchWithTimeout = (url, options = {}, timeout = 5000) => {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timeout')), timeout)
),
]);
};
// Usage example
fetchWithTimeout('https://api.example.com/data', {}, 3000)
.then(response => response.json())
.catch(error => console.error('Error:', error));
Advanced Axios Usage
Interceptor Configuration
import axios from 'axios';
// Create axios instance
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
});
// Request interceptor
api.interceptors.request.use(
(config) => {
// Add auth token
const token = AsyncStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor
api.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
// Handle unauthorized error
console.log('Unauthorized, logging out...');
// Perform logout logic
}
return Promise.reject(error);
}
);
// Usage example
api.get('/user/profile')
.then(data => console.log(data))
.catch(error => console.error(error));
Request Cancellation
import axios from 'axios';
const CancelToken = axios.CancelToken;
let cancel;
// Initiate cancellable request
const fetchData = () => {
// Cancel previous request
if (cancel) {
cancel('Operation canceled by the user.');
}
api.get('/data', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
}),
})
.then(response => console.log(response.data))
.catch(thrown => {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
console.error('Error', thrown);
}
});
};
// Cancel request
const cancelRequest = () => {
if (cancel) {
cancel('User canceled the request');
}
};
React Query for State Management and Caching
Basic Query Example
import { useQuery } from 'react-query';
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
};
const DataComponent = () => {
const { data, error, isLoading, isError } = useQuery('dataKey', fetchData);
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
Cache Control and Refresh
import { useQuery, useQueryClient } from 'react-query';
const DataComponent = () => {
const queryClient = useQueryClient();
const { data, isLoading } = useQuery('dataKey', fetchData, {
staleTime: 5000, // Don’t refetch within 5 seconds
cacheTime: 5 * 60 * 1000, // Keep cache for 5 minutes
});
const refetchData = () => {
queryClient.refetchQueries('dataKey');
};
return (
<div>
{isLoading ? <div>Loading...</div> : <div>Data loaded</div>}
<button onClick={refetchData}>Refresh Data</button>
</div>
);
};
Mutation Operations
import { useMutation, useQueryClient } from 'react-query';
const updateData = async (newData) => {
const response = await fetch('https://api.example.com/data', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newData),
});
return response.json();
};
const UpdateComponent = () => {
const queryClient = useQueryClient();
const mutation = useMutation(updateData, {
onSuccess: () => {
// Invalidate cache and refetch after successful update
queryClient.invalidateQueries('dataKey');
},
});
const handleSubmit = () => {
mutation.mutate({ key: 'value' });
};
return (
<div>
{mutation.isLoading ? <div>Saving...</div> : <button onClick={handleSubmit}>Save</button>}
{mutation.isError && <div>Error: {mutation.error.message}</div>}
</div>
);
};
Data Persistence (AsyncStorage)
Basic Storage Operations
import AsyncStorage from '@react-native-async-storage/async-storage';
// Store data
const storeData = async (key, value) => {
try {
await AsyncStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error('Error storing data:', error);
}
};
// Retrieve data
const getData = async (key) => {
try {
const value = await AsyncStorage.getItem(key);
return value != null ? JSON.parse(value) : null;
} catch (error) {
console.error('Error reading data:', error);
return null;
}
};
// Delete data
const removeData = async (key) => {
try {
await AsyncStorage.removeItem(key);
} catch (error) {
console.error('Error removing data:', error);
}
};
// Usage example
storeData('userData', { name: 'John', age: 30 });
getData('userData').then(data => console.log(data));
Batch Operations and Transactions
import AsyncStorage from '@react-native-async-storage/async-storage';
// Batch store
const storeMultipleData = async () => {
try {
await AsyncStorage.multiSet([
['user1', JSON.stringify({ name: 'Alice' })],
['user2', JSON.stringify({ name: 'Bob' })],
]);
} catch (error) {
console.error('Error storing multiple data:', error);
}
};
// Batch retrieve
const getMultipleData = async () => {
try {
const keys = ['user1', 'user2'];
const results = await AsyncStorage.multiGet(keys);
return results.map(([key, value]) => ({
key,
value: value != null ? JSON.parse(value) : null,
}));
} catch (error) {
console.error('Error reading multiple data:', error);
return [];
}
};
// Batch delete
const removeMultipleData = async () => {
try {
await AsyncStorage.multiRemove(['user1', 'user2']);
} catch (error) {
console.error('Error removing multiple data:', error);
}
};
WebSocket and Real-Time Data Updates
Basic WebSocket Connection
import React, { useEffect, useState } from 'react';
const WebSocketComponent = () => {
const [messages, setMessages] = useState([]);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
// Create WebSocket connection
const ws = new WebSocket('wss://api.example.com/ws');
// On connection open
ws.onopen = () => {
console.log('WebSocket connected');
setIsConnected(true);
};
// On message received
ws.onmessage = (event) => {
const newMessage = JSON.parse(event.data);
setMessages((prevMessages) => [...prevMessages, newMessage]);
};
// On connection close
ws.onclose = () => {
console.log('WebSocket disconnected');
setIsConnected(false);
// Implement reconnect logic here if needed
};
// On error
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
// Close connection on component unmount
return () => {
ws.close();
};
}, []);
const sendMessage = (message) => {
if (isConnected) {
ws.send(JSON.stringify(message));
} else {
console.log('Cannot send message, WebSocket is not connected');
}
};
return (
<div>
<div>Connection status: {isConnected ? 'Connected' : 'Disconnected'}</div>
<div>
<button onClick={() => sendMessage({ type: 'ping' })}>Send Ping</button>
</div>
<div>
<h3>Messages:</h3>
<ul>
{messages.map((msg, index) => (
<li key={index}>{JSON.stringify(msg)}</li>
))}
</ul>
</div>
</div>
);
};
WebSocket with Reconnect Mechanism
import React, { useEffect, useState } from 'react';
const ReconnectableWebSocket = () => {
const [messages, setMessages] = useState([]);
const [isConnected, setIsConnected] = useState(false);
const [reconnectAttempts, setReconnectAttempts] = useState(0);
const maxReconnectAttempts = 5;
const reconnectDelay = 3000; // 3 seconds
useEffect(() => {
let ws;
let reconnectTimer;
const connect = () => {
ws = new WebSocket('wss://api.example.com/ws');
ws.onopen = () => {
console.log('WebSocket connected');
setIsConnected(true);
setReconnectAttempts(0); // Reset reconnect counter
};
ws.onmessage = (event) => {
const newMessage = JSON.parse(event.data);
setMessages((prevMessages) => [...prevMessages, newMessage]);
};
ws.onclose = () => {
console.log('WebSocket disconnected');
setIsConnected(false);
if (reconnectAttempts < maxReconnectAttempts) {
setReconnectAttempts((prev) => prev + 1);
reconnectTimer = setTimeout(connect, reconnectDelay);
}
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
};
connect();
return () => {
if (ws) ws.close();
if (reconnectTimer) clearTimeout(reconnectTimer);
};
}, [reconnectAttempts, maxReconnectAttempts]);
return (
<div>
<div>Connection status: {isConnected ? 'Connected' : 'Disconnected'}</div>
<div>Reconnect attempts: {reconnectAttempts}</div>
{/* Message display and send logic same as above */}
</div>
);
};
Platform Integration
Camera and Gallery Access (react-native-camera)
Basic Camera Functionality
import React, { useState, useEffect } from 'react';
import { View, Button, Image } from 'react-native';
import { Camera } from 'react-native-camera';
const CameraComponent = () => {
const [cameraRef, setCameraRef] = useState(null);
const [photo, setPhoto] = useState(null);
const takePicture = async () => {
if (cameraRef) {
const options = { quality: 0.5, base64: true };
const data = await cameraRef.takePictureAsync(options);
setPhoto(data.uri);
}
};
return (
<View style={{ flex: 1 }}>
{!photo ? (
<Camera
ref={ref => setCameraRef(ref)}
style={{ flex: 1 }}
type={Camera.Constants.Type.back}
>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}
>
<Button
title="Take Picture"
onPress={takePicture}
/>
</View>
</Camera>
) : (
<View style={{ flex: 1 }}>
<Image
source={{ uri: photo }}
style={{ flex: 1 }}
/>
<Button
title="Retake"
onPress={() => setPhoto(null)}
/>
</View>
)}
</View>
);
};
Gallery Access
import React, { useState } from 'react';
import { View, Button, Image } from 'react-native';
import * as ImagePicker from 'react-native-image-picker';
const ImagePickerComponent = () => {
const [image, setImage] = useState(null);
const pickImage = () => {
const options = {
mediaType: 'photo',
includeBase64: false,
maxHeight: 2000,
maxWidth: 2000,
};
ImagePicker.launchImageLibrary(options, (response) => {
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else {
setImage(response.uri);
}
});
};
return (
<View style={{ flex: 1 }}>
{!image ? (
<Button
title="Pick an image from camera roll"
onPress={pickImage}
/>
) : (
<View style={{ flex: 1 }}>
<Image
source={{ uri: image }}
style={{ flex: 1 }}
/>
<Button
title="Pick another image"
onPress={() => setImage(null)}
/>
</View>
)}
</View>
);
};
Geolocation Services (react-native-geolocation)
Getting Current Location
import React, { useState, useEffect } from 'react';
import { View, Text, Button } from 'react-native';
import Geolocation from 'react-native-geolocation-service';
import { PermissionsAndroid, Platform } from 'react-native';
const LocationComponent = () => {
const [location, setLocation] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
requestLocationPermission();
}, []);
const requestLocationPermission = async () => {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Location Permission',
message: 'This app needs access to your location',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
getCurrentLocation();
} else {
setError('Location permission denied');
}
} catch (err) {
console.warn(err);
}
} else {
getCurrentLocation();
}
};
const getCurrentLocation = () => {
Geolocation.getCurrentPosition(
(position) => {
setLocation(position.coords);
},
(error) => {
setError(error.message);
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 },
);
};
return (
<View style={{ flex: 1 }}>
{location ? (
<View>
<Text>Latitude: {location.latitude}</Text>
<Text>Longitude: {location.longitude}</Text>
<Text>Accuracy: {location.accuracy} meters</Text>
</View>
) : error ? (
<Text>Error: {error}</Text>
) : (
<Button
title="Get Current Location"
onPress={getCurrentLocation}
/>
)}
</View>
);
};
Continuous Location Updates
import React, { useState, useEffect } from 'react';
import { View, Text, Button } from 'react-native';
import Geolocation from 'react-native-geolocation-service';
import { PermissionsAndroid, Platform } from 'react-native';
const LocationTrackingComponent = () => {
const [location, setLocation] = useState(null);
const [error, setError] = useState(null);
const [watchId, setWatchId] = useState(null);
useEffect(() => {
requestLocationPermission();
return () => {
if (watchId !== null) {
Geolocation.clearWatch(watchId);
}
};
}, []);
const requestLocationPermission = async () => {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Location Permission',
message: 'This app needs access to your location',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
startLocationTracking();
} else {
setError('Location permission denied');
}
} catch (err) {
console.warn(err);
}
} else {
startLocationTracking();
}
};
const startLocationTracking = () => {
const id = Geolocation.watchPosition(
(position) => {
setLocation(position.coords);
},
(error) => {
setError(error.message);
},
{
enableHighAccuracy: true,
distanceFilter: 10, // Update when position changes by 10 meters
interval: 5000, // Check location every 5 seconds
fastestInterval: 2000, // Fastest update every 2 seconds
},
);
setWatchId(id);
};
const stopLocationTracking = () => {
if (watchId !== null) {
Geolocation.clearWatch(watchId);
setWatchId(null);
}
};
return (
<View style={{ flex: 1 }}>
{location ? (
<View>
<Text>Latitude: {location.latitude}</Text>
<Text>Longitude: {location.longitude}</Text>
<Button
title="Stop Tracking"
onPress={stopLocationTracking}
/>
</View>
) : error ? (
<Text>Error: {error}</Text>
) : (
<Button
title="Start Location Tracking"
onPress={startLocationTracking}
/>
)}
</View>
);
};
Push Notifications (react-native-push-notification)
Basic Push Notifications
import React, { useEffect } from 'react';
import { View, Button } from 'react-native';
import PushNotification from 'react-native-push-notification';
const PushNotificationComponent = () => {
useEffect(() => {
// Initialize push notifications
PushNotification.configure({
// (optional) Called when notification is clicked
onNotification: function (notification) {
console.log('NOTIFICATION:', notification);
},
// (optional) Called when token is registered
onRegister: function (token) {
console.log('TOKEN:', token);
},
// Request permissions (required for iOS)
permissions: {
alert: true,
badge: true,
sound: true,
},
// Pop initial notification on app start
popInitialNotification: true,
// Clear notification after click
requestPermissions: true,
});
}, []);
const scheduleLocalNotification = () => {
PushNotification.localNotification({
title: 'My Notification Title',
message: 'My Notification Message',
playSound: true,
soundName: 'default',
});
};
const scheduleDelayedNotification = () => {
PushNotification.localNotificationSchedule({
message: 'Delayed Notification',
date: new Date(Date.now() + 10 * 1000), // After 10 seconds
});
};
return (
<View style={{ flex: 1 }}>
<Button
title="Schedule Local Notification"
onPress={scheduleLocalNotification}
/>
<Button
title="Schedule Delayed Notification"
onPress={scheduleDelayedNotification}
/>
</View>
);
};
Handling Remote Push Notifications
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import PushNotification from 'react-native-push-notification';
import messaging from '@react-native-firebase/messaging';
const RemotePushNotificationComponent = () => {
useEffect(() => {
// Request notification permissions
requestUserPermission();
// Listen for messages
messaging().onMessage(async (remoteMessage) => {
console.log('A new FCM message arrived!', JSON.stringify(remoteMessage));
// Show local notification
PushNotification.localNotification({
title: remoteMessage.notification.title,
message: remoteMessage.notification.body,
});
});
// Handle notification clicks
messaging().onNotificationOpenedApp(remoteMessage => {
console.log('Notification caused app to open from background state:', remoteMessage.notification);
});
// Check if app opened from notification
messaging()
.getInitialNotification()
.then(remoteMessage => {
if (remoteMessage) {
console.log('Notification caused app to open from quit state:', remoteMessage.notification);
}
});
return () => {
// Clean up listeners
};
}, []);
const requestUserPermission = async () => {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('Authorization status:', authStatus);
}
};
return (
<View style={{ flex: 1 }}>
<Text>Remote Push Notification Example</Text>
</View>
);
};
Local Storage and File System (react-native-fs)
Basic File Operations
import React, { useState } from 'react';
import { View, Button, Text } from 'react-native';
import RNFS from 'react-native-fs';
const FileSystemComponent = () => {
const [filePath, setFilePath] = useState(null);
const [fileContent, setFileContent] = useState(null);
const createFile = async () => {
const path = RNFS.DocumentDirectoryPath + '/test.txt';
const content = 'Hello, React Native FS!';
try {
await RNFS.writeFile(path, content, 'utf8');
setFilePath(path);
console.log('File created at:', path);
} catch (error) {
console.error('Error creating file:', error);
}
};
const readFile = async () => {
if (!filePath) {
console.log('No file path specified');
return;
}
try {
const content = await RNFS.readFile(filePath, 'utf8');
setFileContent(content);
console.log('File content:', content);
} catch (error) {
console.error('Error reading file:', error);
}
};
const appendToFile = async () => {
if (!filePath) {
console.log('No file path specified');
return;
}
const additionalContent = '\nAppended content';
try {
await RNFS.appendFile(filePath, additionalContent, 'utf8');
console.log('Content appended to file');
} catch (error) {
console.error('Error appending to file:', error);
}
};
const deleteFile = async () => {
if (!filePath) {
console.log('No file path specified');
return;
}
try {
await RNFS.unlink(filePath);
setFilePath(null);
setFileContent(null);
console.log('File deleted');
} catch (error) {
console.error('Error deleting file:', error);
}
};
return (
<View style={{ flex: 1 }}>
<Button title="Create File" onPress={createFile} />
<Button title="Read File" onPress={readFile} />
<Button title="Append to File" onPress={appendToFile} />
<Button title="Delete File" onPress={deleteFile} />
{filePath && <Text>File Path: {filePath}</Text>}
{fileContent && <Text>File Content: {fileContent}</Text>}
</View>
);
};
File Download
import React, { useState } from 'react';
import { View, Button, Text } from 'react-native';
import RNFS from 'react-native-fs';
const FileDownloadComponent = () => {
const [downloadProgress, setDownloadProgress] = useState(0);
const [downloadedFilePath, setDownloadedFilePath] = useState(null);
const downloadFile = async () => {
const downloadDest = `${RNFS.DocumentDirectoryPath}/downloaded_file.pdf`;
const options = {
fromUrl: 'https://example.com/sample.pdf',
toFile: downloadDest,
background: true,
begin: (res) => {
console.log('Download started', res);
},
progress: (res) => {
const progress = (res.bytesWritten / res.contentLength) * 100;
setDownloadProgress(progress);
console.log('Download progress:', progress);
},
};
try {
await RNFS.downloadFile(options).promise;
setDownloadedFilePath(downloadDest);
console.log('File downloaded to:', downloadDest);
} catch (error) {
console.error('Error downloading file:', error);
}
};
return (
<View style={{ flex: 1 }}>
<Button title="Download File" onPress={downloadFile} />
{downloadProgress > 0 && (
<Text>Download Progress: {downloadProgress.toFixed(2)}%</Text>
)}
{downloadedFilePath && (
<Text>File downloaded to: {downloadedFilePath}</Text>
)}
</View>
);
};
Third-Party SDK Integration (Payments, Social Login)
Payment Integration Example (Stripe)
import React, { useState } from 'react';
import { View, Button, TextInput } from 'react-native';
import StripeTerminal from '@stripe/stripe-terminal-react-native';
const PaymentComponent = () => {
const [paymentStatus, setPaymentStatus] = useState('Idle');
const [amount, setAmount] = useState('1000'); // Amount in cents
const initializeReader = async () => {
try {
setPaymentStatus('Initializing...');
await StripeTerminal.discoverReaders({
discoveryMethod: 'BluetoothProximity',
});
setPaymentStatus('Reader discovered');
} catch (error) {
setPaymentStatus(`Error: ${error.message}`);
}
};
const collectPaymentMethod = async () => {
try {
setPaymentStatus('Collecting payment method...');
const paymentIntent = await StripeTerminal.createPaymentIntent({
amount: parseInt(amount, 10),
currency: 'usd',
});
const result = await StripeTerminal.collectPaymentMethod(paymentIntent);
if (result.paymentStatus === 'Success') {
setPaymentStatus('Payment successful');
} else {
setPaymentStatus('Payment failed');
}
} catch (error) {
setPaymentStatus(`Error: ${error.message}`);
}
};
return (
<View style={{ flex: 1 }}>
<TextInput
value={amount}
onChangeText={setAmount}
placeholder="Amount in cents"
keyboardType="numeric"
/>
<Button title="Initialize Reader" onPress={initializeReader} />
<Button title="Collect Payment" onPress={collectPaymentMethod} />
<Text>Payment Status: {paymentStatus}</Text>
</View>
);
};
Social Login Integration (Facebook)
import React, { useState } from 'react';
import { View, Button, Text } from 'react-native';
import { LoginManager, AccessToken } from 'react-native-fbsdk-next';
const FacebookLoginComponent = () => {
const [userInfo, setUserInfo] = useState(null);
const [error, setError] = useState(null);
const loginWithFacebook = async () => {
try {
// Request login
const result = await LoginManager.logInWithPermissions(['public_profile', 'email']);
if (result.isCancelled) {
setError('Login was cancelled');
return;
}
// Get access token
const data = await AccessToken.getCurrentAccessToken();
if (!data) {
setError('Something went wrong obtaining the access token');
return;
}
// Fetch user info using access token
const response = await fetch(
`https://graph.facebook.com/v12.0/me?fields=id,name,email&access_token=${data.accessToken}`
);
const userData = await response.json();
setUserInfo(userData);
setError(null);
} catch (e) {
setError(e.message);
setUserInfo(null);
}
};
const logoutFromFacebook = async () => {
await LoginManager.logOut();
setUserInfo(null);
setError(null);
};
return (
<View style={{ flex: 1 }}>
{!userInfo ? (
<Button title="Login with Facebook" onPress={loginWithFacebook} />
) : (
<View>
<Text>Welcome, {userInfo.name}!</Text>
<Text>Email: {userInfo.email}</Text>
<Button title="Logout" onPress={logoutFromFacebook} />
</View>
)}
{error && <Text style={{ color: 'red' }}>Error: {error}</Text>}
</View>
);
};



