Lesson 02-React Native Fundamentals

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
    Create BatteryModule.java in android/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
    Create BatteryPackage.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:
  • @ReactMethod exposes methods to JS.
  • Promise handles 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 createViewManagers in BatteryPackage.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 with flex properties.
    • 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: 1 makes the container fill the screen.
      • flexDirection: 'row' sets horizontal layout.
      • justifyContent and alignItems center 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: flexDirection defaults to column (unlike Web’s row).

    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 (cover crops to fill, contain shows fully).
      • onLoad: Callback for image load completion.

    Caching and Optimization

    • Caching is enabled by default; control via cache property (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.create creates style objects, reducing runtime computation.
      • Uses camelCase (e.g., fontSize instead of font-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.active applies 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

    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>
      );
    };
    
    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>
      );
    };
    
    Share your love