Project Architecture Design
Single Page Application (SPA) vs. Multi-Page Application Architecture
SPA Architecture Implementation
// App.js - Single Page Application Entry
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
import DetailScreen from './screens/DetailScreen';
const Stack = createStackNavigator();
const App = () => (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
export default App;
Multi-Page Application Architecture Design
// Multi-Page Application Configuration Example (Achieved via Multiple Entry Files)
// app1/App.js
import React from 'react';
import { View, Text } from 'react-native';
const App1 = () => <View><Text>App 1</Text></View>;
export default App1;
// app2/App.js
import React from 'react';
import { View, Text } from 'react-native';
const App2 = () => <View><Text>App 2</Text></View>;
export default App2;
// Implement Multiple Entries via Different Build Configurations
// metro.config.js
module.exports = {
resolver: {
extraNodeModules: {
app1: path.resolve(__dirname, 'app1'),
app2: path.resolve(__dirname, 'app2'),
},
},
};
Modular and Component-Based Design Principles
Functional Module Division Example
src/
├── modules/
│ ├── auth/ # Authentication Module
│ │ ├── components/ # Module-Specific Components
│ │ ├── screens/ # Module-Specific Screens
│ │ ├── hooks/ # Module-Specific Custom Hooks
│ │ └── api.js # Module API Requests
│ ├── product/ # Product Module
│ └── order/ # Order Module
├── shared/ # Shared Module
│ ├── components/ # Global Shared Components
│ ├── utils/ # Utility Functions
│ └── constants/ # Global Constants
Component Design Specifications
// High Cohesion, Low Coupling Component Example
// components/Button/index.js
import React from 'react';
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
const Button = ({ title, onPress, style, textStyle }) => (
<TouchableOpacity style={[styles.container, style]} onPress={onPress}>
<Text style={[styles.text, textStyle]}>{title}</Text>
</TouchableOpacity>
);
const styles = StyleSheet.create({
container: {
padding: 12,
borderRadius: 8,
backgroundColor: '#2196F3',
},
text: {
color: 'white',
fontSize: 16,
},
});
export default Button;
State Management Architecture Design (Redux, MobX)
Redux Architecture Implementation
// store/configureStore.js
import { configureStore } from '@reduxjs/toolkit';
import authReducer from '../modules/authSlice';
export const store = configureStore({
reducer: {
auth: authReducer,
},
});
// modules/authSlice.js
import { createSlice } from '@reduxjs/toolkit';
const authSlice = createSlice({
name: 'auth',
initialState: { isLoggedIn: false },
reducers: {
login: (state) => { state.isLoggedIn = true; },
logout: (state) => { state.isLoggedIn = false; },
},
});
export const { login, logout } = authSlice.actions;
export default authSlice.reducer;
MobX Architecture Implementation
// stores/AuthStore.js
import { makeAutoObservable } from 'mobx';
class AuthStore {
isLoggedIn = false;
constructor() {
makeAutoObservable(this);
}
login() {
this.isLoggedIn = true;
}
logout() {
this.isLoggedIn = false;
}
}
export default new AuthStore();
Routing Architecture Design (React Navigation)
Nested Routing Configuration
// navigation/AppNavigator.js
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import HomeStack from './HomeStack';
import ProfileStack from './ProfileStack';
import SettingsScreen from '../screens/SettingsScreen';
const Tab = createBottomTabNavigator();
const Stack = createStackNavigator();
const HomeTabs = () => (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStack} />
<Tab.Screen name="Profile" component={ProfileStack} />
</Tab.Navigator>
);
const AppNavigator = () => (
<Stack.Navigator>
<Stack.Screen name="Main" component={HomeTabs} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
);
export default AppNavigator;
Dynamic Routing Configuration
// Dynamic Routing Example (Based on User Permissions)
const getRoutesForUser = (userRole) => {
const commonRoutes = [
{ name: 'Home', component: HomeScreen },
{ name: 'Profile', component: ProfileScreen },
];
if (userRole === 'admin') {
return [...commonRoutes, { name: 'Admin', component: AdminScreen }];
}
return commonRoutes;
};
const DynamicNavigator = ({ userRole }) => {
const routes = getRoutesForUser(userRole);
return (
<Stack.Navigator>
{routes.map(route => (
<Stack.Screen
key={route.name}
name={route.name}
component={route.component}
/>
))}
</Stack.Navigator>
);
};
Project Directory Structure and Standards
Recommended Directory Structure
my-app/
├── android/ # Android Native Code
├── ios/ # iOS Native Code
├── src/
│ ├── assets/ # Static Assets (Images, Fonts, etc.)
│ ├── components/ # Global Shared Components
│ ├── modules/ # Functional Modules
│ │ ├── auth/ # Authentication Module
│ │ ├── product/ # Product Module
│ │ └── order/ # Order Module
│ ├── navigation/ # Routing Configuration
│ ├── screens/ # Screen Components
│ ├── store/ # State Management
│ ├── styles/ # Global Styles
│ ├── utils/ # Utility Functions
│ ├── App.js # Application Entry
│ └── index.js # Render Entry
├── .eslintrc.js # ESLint Configuration
├── .prettierrc # Prettier Configuration
├── babel.config.js # Babel Configuration
├── metro.config.js # Metro Configuration
└── package.json
File Naming Conventions
1. Component Files:
- PascalCase naming (e.g., `Button.js`)
- Directory structure: `components/Button/index.js + components/Button/Button.styles.js`
2. Screen Files:
- PascalCase naming (e.g., `HomeScreen.js`)
- Directory structure: `screens/HomeScreen/index.js`
3. Utility Functions:
- camelCase naming (e.g., `formatDate.js`)
- Directory structure: `utils/formatDate.js`
4. Style Files:
- Same directory as component, named `ComponentName.styles.js`
Advanced Configuration for React Native CLI
Custom Environment Variables
// .env File
API_URL=https://api.example.com
APP_VERSION=1.0.0
// Access Environment Variables Using react-native-config
import Config from 'react-native-config';
console.log(Config.API_URL); // Output: https://api.example.com
Custom Build Scripts
// package.json
{
"scripts": {
"start": "react-native start",
"android": "react-native run-android",
"ios": "react-native run-ios",
"build:android": "cd android && ./gradlew assembleRelease",
"build:ios": "cd ios && xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release",
"lint": "eslint src --ext .js,.jsx,.ts,.tsx",
"test": "jest"
}
}
Deep Configuration for Metro Bundler
Cache Optimization Configuration
// metro.config.js
module.exports = {
cacheStores: [
new MetroCache.FileStore({
root: path.join(__dirname, 'node_modules', '.cache', 'metro-cache'),
}),
],
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true, // Enable inline requires to optimize startup performance
},
}),
},
maxWorkers: 4, // Adjust based on CPU cores
};
Custom Bundling Configuration
// metro.config.js
const { getDefaultConfig } = require('metro-config');
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts },
} = await getDefaultConfig();
return {
resolver: {
sourceExts: [...sourceExts, 'jsx', 'tsx'],
assetExts: assetExts.filter(ext => ext !== 'svg'),
extraNodeModules: {
// Custom module resolution
},
},
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
};
})();
TypeScript Integration and Configuration
TypeScript Configuration
// tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"lib": ["dom", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-native",
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"],
"@screens/*": ["src/screens/*"]
}
},
"include": ["src"],
"exclude": ["node_modules"]
}
Type Definition Example
// types/api.d.ts
declare namespace API {
interface User {
id: string;
name: string;
email: string;
}
type ApiResponse<T> = {
data: T;
error?: string;
};
}
// Usage Example
import { API } from '../types/api';
const fetchUser = async (): Promise<API.ApiResponse<API.User>> => {
// API call implementation
};
Code Standards and Style Guidelines
ESLint Configuration
// .eslintrc.js
module.exports = {
root: true,
extends: [
'@react-native-community',
'plugin:prettier/recommended',
],
rules: {
'react-native/no-inline-styles': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn',
},
settings: {
'import/resolver': {
node: {
paths: ['src'],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
};
Prettier Configuration
// .prettierrc
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "avoid"
}
Automated Testing Framework
Jest Unit Testing Example
// __tests__/Button.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import Button from '../components/Button';
describe('Button Component', () => {
it('renders correctly', () => {
const { getByText } = render(<Button title="Test" />);
expect(getByText('Test')).toBeTruthy();
});
it('calls onPress when pressed', () => {
const onPressMock = jest.fn();
const { getByText } = render(
<Button title="Test" onPress={onPressMock} />
);
fireEvent.press(getByText('Test'));
expect(onPressMock).toHaveBeenCalled();
});
});
Detox End-to-End Testing Example
// e2e/login.spec.js
describe('Login Flow', () => {
beforeAll(async () => {
await device.launchApp();
});
it('should login successfully', async () => {
await element(by.id('emailInput')).typeText('user@example.com');
await element(by.id('passwordInput')).typeText('password123');
await element(by.id('loginButton')).tap();
await expect(element(by.text('Welcome'))).toBeVisible();
});
});
Code Splitting and Lazy Loading
Dynamic Import Implementation
// Use React.lazy for Component Lazy Loading
import React, { Suspense } from 'react';
const LazyComponent = React.lazy(() => import('./components/HeavyComponent'));
const App = () => (
<Suspense fallback={<ActivityIndicator />}>
<LazyComponent />
</Suspense>
);
// Route-Level Code Splitting
const HomeScreen = React.lazy(() => import('../screens/HomeScreen'));
const ProfileScreen = React.lazy(() => import('../screens/ProfileScreen'));
const AppNavigator = () => (
<Stack.Navigator>
<Stack.Screen name="Home">
{props => (
<Suspense fallback={<ActivityIndicator />}>
<HomeScreen {...props} />
</Suspense>
)}
</Stack.Screen>
<Stack.Screen name="Profile">
{props => (
<Suspense fallback={<ActivityIndicator />}>
<ProfileScreen {...props} />
</Suspense>
)}
</Stack.Screen>
</Stack.Navigator>
);
Resource Compression and Caching Strategies
Image Optimization Configuration
// Use react-native-fast-image for Optimized Image Loading
import FastImage from 'react-native-fast-image';
const OptimizedImage = () => (
<FastImage
style={{ width: 200, height: 200 }}
source={{
uri: 'https://example.com/image.jpg',
priority: FastImage.priority.high,
cache: FastImage.cacheControl.immutable,
}}
resizeMode={FastImage.resizeMode.contain}
/>
);
// Android Resource Compression (Gradle Configuration)
// android/app/build.gradle
android {
aaptOptions {
cruncherEnabled = false // Disable PNG compression (use WebP instead)
}
}
Caching Strategy Implementation
// Network Request Caching Strategy
import { CacheManager } from 'react-native-expo-image-cache';
const getImage = async (url) => {
const path = await CacheManager.get(url).getPath();
if (path) {
return path; // Return cached path
}
// Download and cache image
await CacheManager.downloadFile({
fromUrl: url,
toFile: `${CacheManager.getCacheDirectory()}${md5(url)}`,
cache: true,
background: true,
});
return await CacheManager.get(url).getPath();
};
Application Packaging and Release
Android APK Packaging
# Generate Signing Key (if not already created)
keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
# Configure Gradle Signing (gradle.properties)
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****
# Execute Build Command
cd android
./gradlew assembleRelease
iOS IPA Packaging
# Package Using Xcode
1. Open ios/MyApp.xcworkspace
2. Select Product > Archive
3. In Organizer window, select Archive and click Distribute App
4. Choose distribution method (App Store/Ad Hoc/Development)
# Command Line Packaging (requires certificate configuration)
xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Release -archivePath build/MyApp.xcarchive archive
xcodebuild -exportArchive -archivePath build/MyApp.xcarchive -exportOptionsPlist ios/exportOptions.plist -exportPath build
Flipper Integration
// Install Flipper Plugin
yarn add react-native-flipper
// Initialize Flipper (react-native.config.js)
module.exports = {
plugins: ['react-native-flipper'],
};
// Use Performance Monitoring Plugin
import PerfMonitor from 'react-native-performance-monitor';
PerfMonitor.start();
// ...application code
PerfMonitor.stop();
Sentry Error Monitoring
// Install Sentry
yarn add @sentry/react-native
// Initialize Sentry
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'https://your-dsn@sentry.io/project-id',
enableNative: true,
debug: __DEV__,
});
// Manually Capture Errors
try {
// Code that might throw an error
} catch (error) {
Sentry.captureException(error);
}
// Log Messages
Sentry.captureMessage('Something went wrong');
Hot Updates and OTA Updates
CodePush Configuration
// Install CodePush
yarn add react-native-code-push
// Link Native Dependencies
react-native link react-native-code-push
// Initialize CodePush (App.js)
import codePush from 'react-native-code-push';
const App = () => {
// ...application code
};
export default codePush({
checkFrequency: codePush.CheckFrequency.ON_APP_START,
installMode: codePush.InstallMode.ON_NEXT_RESTART,
})(App);
// Release Update (Command Line)
code-push release-react MyApp android
code-push release-react MyApp ios
Custom OTA Update Solution
// Implement Custom Update Check Logic
const checkForUpdates = async () => {
try {
const response = await fetch('https://your-api.com/updates/latest');
const { version, url } = await response.json();
const currentVersion = DeviceInfo.getVersion();
if (compareVersions(version, currentVersion) > 0) {
// Prompt user to update
Alert.alert(
'New Version Available',
'Download and install the new version now?',
[
{ text: 'Later', style: 'cancel' },
{ text: 'Update', onPress: () => downloadUpdate(url) },
]
);
}
} catch (error) {
console.error('Failed to check for updates:', error);
}
};
const downloadUpdate = async (url) => {
// Implement download and install logic
// Android: Use react-native-fs to download APK and call Intent to install
// iOS: Requires enterprise certificate or TestFlight distribution
};