Exemples React Native
Exemples de développement mobile multiplateforme React Native incluant composants, navigation, gestion d'état et intégration native
Key Facts
- Category
- Mobile Development
- Items
- 4
- Format Families
- text
Sample Overview
Exemples de développement mobile multiplateforme React Native incluant composants, navigation, gestion d'état et intégration native This sample set belongs to Mobile Development and can be used to test related workflows inside Elysia Tools.
💻 Bonjour Monde React Native typescript
Exemples d'applications React Native de base avec composants, styles et configuration de navigation
// React Native Hello World Examples
import React, { useState } from 'react';
import {
StyleSheet,
Text,
View,
Button,
TextInput,
TouchableOpacity,
Image,
ScrollView,
Alert,
SafeAreaView,
StatusBar,
Platform
} from 'react-native';
// 1. Basic Hello World Component
const HelloWorld = () => {
return (
<View style={styles.container}>
<Text style={styles.title}>Hello, World!</Text>
<Text style={styles.subtitle}>Welcome to React Native</Text>
</View>
);
};
// 2. Component with Props
interface GreetingProps {
name: string;
greeting?: string;
}
const Greeting: React.FC<GreetingProps> = ({ name, greeting = 'Hello' }) => {
return (
<View style={styles.card}>
<Text style={styles.greeting}>{greeting}, {name}!</Text>
<Text style={styles.message}>This component accepts props</Text>
</View>
);
};
// 3. Interactive Counter Component
const Counter: React.FC = () => {
const [count, setCount] = useState(0);
return (
<View style={styles.card}>
<Text style={styles.counterText}>Count: {count}</Text>
<View style={styles.buttonRow}>
<TouchableOpacity
style={styles.button}
onPress={() => setCount(count - 1)}
>
<Text style={styles.buttonText}>-</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={() => setCount(0)}
>
<Text style={styles.buttonText}>Reset</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => setCount(count + 1)}
>
<Text style={styles.buttonText}>+</Text>
</TouchableOpacity>
</View>
</View>
);
};
// 4. User Profile Component
interface UserProfileProps {
initialName?: string;
initialAge?: number;
}
const UserProfile: React.FC<UserProfileProps> = ({
initialName = '',
initialAge = 0
}) => {
const [name, setName] = useState(initialName);
const [age, setAge] = useState(initialAge.toString());
const [isEditing, setIsEditing] = useState(false);
const handleSave = () => {
setIsEditing(false);
Alert.alert('Profile Saved', `Name: ${name}, Age: ${age}`);
};
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>User Profile</Text>
{isEditing ? (
<View>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder="Enter your name"
/>
<TextInput
style={styles.input}
value={age}
onChangeText={setAge}
placeholder="Enter your age"
keyboardType="numeric"
/>
<View style={styles.buttonRow}>
<TouchableOpacity
style={[styles.button, styles.saveButton]}
onPress={handleSave}
>
<Text style={styles.buttonText}>Save</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.cancelButton]}
onPress={() => setIsEditing(false)}
>
<Text style={styles.buttonText}>Cancel</Text>
</TouchableOpacity>
</View>
</View>
) : (
<View>
<Text style={styles.profileText}>Name: {name || 'Not set'}</Text>
<Text style={styles.profileText}>Age: {age || 'Not set'}</Text>
<TouchableOpacity
style={[styles.button, styles.editButton]}
onPress={() => setIsEditing(true)}
>
<Text style={styles.buttonText}>Edit Profile</Text>
</TouchableOpacity>
</View>
)}
</View>
);
};
// 5. Todo List Component
interface Todo {
id: number;
text: string;
completed: boolean;
createdAt: Date;
}
const TodoApp: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>([
{ id: 1, text: 'Learn React Native', completed: false, createdAt: new Date() },
{ id: 2, text: 'Build a mobile app', completed: false, createdAt: new Date() }
]);
const [newTodoText, setNewTodoText] = useState('');
const addTodo = () => {
if (newTodoText.trim()) {
const newTodo: Todo = {
id: Date.now(),
text: newTodoText.trim(),
completed: false,
createdAt: new Date()
};
setTodos([newTodo, ...todos]);
setNewTodoText('');
}
};
const toggleTodo = (id: number) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
const deleteTodo = (id: number) => {
Alert.alert(
'Delete Todo',
'Are you sure you want to delete this todo?',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Delete',
style: 'destructive',
onPress: () => setTodos(todos.filter(todo => todo.id !== id))
}
]
);
};
const completedCount = todos.filter(todo => todo.completed).length;
const activeCount = todos.filter(todo => !todo.completed).length;
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>Todo List</Text>
<View style={styles.inputContainer}>
<TextInput
style={styles.todoInput}
value={newTodoText}
onChangeText={setNewTodoText}
placeholder="What needs to be done?"
onSubmitEditing={addTodo}
/>
<TouchableOpacity style={styles.addButton} onPress={addTodo}>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
</View>
<View style={styles.statsContainer}>
<Text style={styles.statText}>
{activeCount} active, {completedCount} completed
</Text>
</View>
<ScrollView style={styles.todoList}>
{todos.map((todo) => (
<View key={todo.id} style={styles.todoItem}>
<TouchableOpacity
style={styles.todoCheckbox}
onPress={() => toggleTodo(todo.id)}
>
<View style={[
styles.checkbox,
todo.completed && styles.checkboxChecked
]}>
{todo.completed && <Text style={styles.checkmark}>✓</Text>}
</View>
</TouchableOpacity>
<View style={styles.todoContent}>
<Text style={[
styles.todoText,
todo.completed && styles.todoTextCompleted
]}>
{todo.text}
</Text>
<Text style={styles.todoDate}>
{todo.createdAt.toLocaleDateString()}
</Text>
</View>
<TouchableOpacity
style={styles.deleteButton}
onPress={() => deleteTodo(todo.id)}
>
<Text style={styles.deleteButtonText}>×</Text>
</TouchableOpacity>
</View>
))}
</ScrollView>
</View>
);
};
// 6. Main App Component
const App: React.FC = () => {
const [activeComponent, setActiveComponent] = useState<string>('all');
const renderComponent = () => {
switch (activeComponent) {
case 'greeting':
return <Greeting name="React Native Developer" />;
case 'counter':
return <Counter />;
case 'profile':
return <UserProfile initialName="John Doe" initialAge={25} />;
case 'todo':
return <TodoApp />;
default:
return (
<>
<HelloWorld />
<Greeting name="Mobile Developer" />
<Counter />
<UserProfile />
<TodoApp />
</>
);
}
};
return (
<SafeAreaView style={styles.safeArea}>
<StatusBar
barStyle="light-content"
backgroundColor="#2c3e50"
/>
<View style={styles.header}>
<Text style={styles.headerTitle}>React Native Demo</Text>
<Text style={styles.headerSubtitle}>
Platform: {Platform.OS === 'ios' ? 'iOS' : 'Android'}
</Text>
</View>
<View style={styles.navigation}>
{['all', 'greeting', 'counter', 'profile', 'todo'].map((component) => (
<TouchableOpacity
key={component}
style={[
styles.navButton,
activeComponent === component && styles.activeNavButton
]}
onPress={() => setActiveComponent(component)}
>
<Text style={[
styles.navButtonText,
activeComponent === component && styles.activeNavButtonText
]}>
{component.charAt(0).toUpperCase() + component.slice(1)}
</Text>
</TouchableOpacity>
))}
</View>
<ScrollView style={styles.content}>
{renderComponent()}
</ScrollView>
</SafeAreaView>
);
};
// Styles
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#f8f9fa',
},
container: {
padding: 20,
alignItems: 'center',
backgroundColor: '#fff',
margin: 16,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#2c3e50',
marginBottom: 8,
},
subtitle: {
fontSize: 16,
color: '#7f8c8d',
textAlign: 'center',
},
card: {
backgroundColor: '#fff',
margin: 16,
padding: 20,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
cardTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#2c3e50',
marginBottom: 16,
textAlign: 'center',
},
greeting: {
fontSize: 20,
fontWeight: '600',
color: '#3498db',
textAlign: 'center',
marginBottom: 8,
},
message: {
fontSize: 14,
color: '#7f8c8d',
textAlign: 'center',
},
counterText: {
fontSize: 24,
fontWeight: 'bold',
color: '#2c3e50',
textAlign: 'center',
marginBottom: 16,
},
buttonRow: {
flexDirection: 'row',
justifyContent: 'center',
gap: 12,
},
button: {
backgroundColor: '#ecf0f1',
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 6,
minWidth: 60,
alignItems: 'center',
},
primaryButton: {
backgroundColor: '#3498db',
},
saveButton: {
backgroundColor: '#27ae60',
},
cancelButton: {
backgroundColor: '#e74c3c',
},
editButton: {
backgroundColor: '#f39c12',
},
buttonText: {
color: '#2c3e50',
fontSize: 16,
fontWeight: '600',
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 6,
padding: 12,
fontSize: 16,
marginBottom: 12,
backgroundColor: '#fff',
},
profileText: {
fontSize: 16,
color: '#2c3e50',
marginBottom: 8,
},
inputContainer: {
flexDirection: 'row',
marginBottom: 16,
},
todoInput: {
flex: 1,
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 6,
padding: 12,
fontSize: 16,
marginRight: 8,
},
addButton: {
backgroundColor: '#27ae60',
width: 48,
height: 48,
borderRadius: 6,
justifyContent: 'center',
alignItems: 'center',
},
addButtonText: {
color: '#fff',
fontSize: 24,
fontWeight: 'bold',
},
statsContainer: {
marginBottom: 16,
padding: 12,
backgroundColor: '#ecf0f1',
borderRadius: 6,
},
statText: {
textAlign: 'center',
color: '#7f8c8d',
fontSize: 14,
},
todoList: {
maxHeight: 300,
},
todoItem: {
flexDirection: 'row',
alignItems: 'center',
padding: 12,
backgroundColor: '#fff',
borderBottomWidth: 1,
borderBottomColor: '#ecf0f1',
},
todoCheckbox: {
marginRight: 12,
},
checkbox: {
width: 24,
height: 24,
borderWidth: 2,
borderColor: '#ddd',
borderRadius: 4,
justifyContent: 'center',
alignItems: 'center',
},
checkboxChecked: {
backgroundColor: '#27ae60',
borderColor: '#27ae60',
},
checkmark: {
color: '#fff',
fontSize: 16,
fontWeight: 'bold',
},
todoContent: {
flex: 1,
},
todoText: {
fontSize: 16,
color: '#2c3e50',
},
todoTextCompleted: {
textDecorationLine: 'line-through',
color: '#7f8c8d',
},
todoDate: {
fontSize: 12,
color: '#95a5a6',
marginTop: 4,
},
deleteButton: {
padding: 8,
backgroundColor: '#e74c3c',
borderRadius: 4,
},
deleteButtonText: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
},
header: {
backgroundColor: '#2c3e50',
padding: 20,
paddingTop: Platform.OS === 'ios' ? 40 : 20,
},
headerTitle: {
fontSize: 24,
fontWeight: 'bold',
color: '#fff',
textAlign: 'center',
},
headerSubtitle: {
fontSize: 14,
color: '#ecf0f1',
textAlign: 'center',
marginTop: 4,
},
navigation: {
flexDirection: 'row',
backgroundColor: '#34495e',
paddingVertical: 8,
},
navButton: {
flex: 1,
paddingVertical: 12,
alignItems: 'center',
},
activeNavButton: {
backgroundColor: '#2c3e50',
},
navButtonText: {
color: '#ecf0f1',
fontSize: 14,
fontWeight: '500',
},
activeNavButtonText: {
color: '#3498db',
fontWeight: 'bold',
},
content: {
flex: 1,
},
});
export default App;
💻 Navigation React Native typescript
Modèles de navigation et routage dans React Native utilisant React Navigation
// React Native Navigation Examples
import React, { useState } from 'react';
import {
StyleSheet,
Text,
View,
Button,
TouchableOpacity,
Image,
ScrollView,
TextInput,
Alert
} from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
NativeStackScreenProps,
} from '@react-navigation/native-stack';
import {
createBottomTabNavigator,
BottomTabNavigationProp,
} from '@react-navigation/bottom-tabs';
import {
createDrawerNavigator,
DrawerNavigationProp,
} from '@react-navigation/drawer';
// Type definitions
type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Settings: undefined;
Details: { itemId: string };
Modal: undefined;
};
type TabParamList = {
HomeTab: undefined;
SearchTab: undefined;
ProfileTab: undefined;
};
type DrawerParamList = {
HomeDrawer: undefined;
SettingsDrawer: undefined;
AboutDrawer: undefined;
};
// 1. Stack Navigation Example
const Stack = createNativeStackNavigator<RootStackParamList>();
interface HomeScreenProps {
navigation: NativeStackNavigationProp<RootStackParamList, 'Home'>;
}
const HomeScreen: React.FC<HomeScreenProps> = ({ navigation }) => {
const [items] = useState([
{ id: '1', title: 'First Item', description: 'This is the first item' },
{ id: '2', title: 'Second Item', description: 'This is the second item' },
{ id: '3', title: 'Third Item', description: 'This is the third item' },
]);
return (
<View style={styles.container}>
<Text style={styles.title}>Home Screen</Text>
<ScrollView style={styles.itemList}>
{items.map((item) => (
<TouchableOpacity
key={item.id}
style={styles.itemCard}
onPress={() => navigation.navigate('Details', { itemId: item.id })}
>
<Text style={styles.itemTitle}>{item.title}</Text>
<Text style={styles.itemDescription}>{item.description}</Text>
<Text style={styles.seeMore}>See details →</Text>
</TouchableOpacity>
))}
</ScrollView>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={() => navigation.navigate('Profile', { userId: 'user123' })}
>
<Text style={styles.buttonText}>Go to Profile</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={() => navigation.navigate('Modal')}
>
<Text style={styles.buttonText}>Open Modal</Text>
</TouchableOpacity>
</View>
</View>
);
};
interface ProfileScreenProps {
route: NativeStackScreenProps<RootStackParamList, 'Profile'>['route'];
navigation: NativeStackNavigationProp<RootStackParamList, 'Profile'>;
}
const ProfileScreen: React.FC<ProfileScreenProps> = ({ route, navigation }) => {
const { userId } = route.params;
const [user, setUser] = useState({
name: 'John Doe',
email: '[email protected]',
bio: 'Mobile app developer'
});
return (
<View style={styles.container}>
<View style={styles.profileHeader}>
<View style={styles.avatar}>
<Text style={styles.avatarText}>
{user.name.split(' ').map(n => n[0]).join('')}
</Text>
</View>
<Text style={styles.profileName}>{user.name}</Text>
<Text style={styles.profileEmail}>{user.email}</Text>
</View>
<View style={styles.profileContent}>
<Text style={styles.sectionTitle}>About</Text>
<Text style={styles.bioText}>{user.bio}</Text>
<Text style={styles.sectionTitle}>User ID</Text>
<Text style={styles.userId}>{userId}</Text>
<TouchableOpacity
style={[styles.button, styles.editButton]}
onPress={() => Alert.alert('Edit Profile', 'Profile editing coming soon!')}
>
<Text style={styles.buttonText}>Edit Profile</Text>
</TouchableOpacity>
</View>
<View style={styles.navButtons}>
<TouchableOpacity
style={[styles.navButton, styles.backButton]}
onPress={() => navigation.goBack()}
>
<Text style={styles.navButtonText}>← Back</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.navButton, styles.settingsButton]}
onPress={() => navigation.navigate('Settings')}
>
<Text style={styles.navButtonText}>Settings →</Text>
</TouchableOpacity>
</View>
</View>
);
};
const DetailsScreen: React.FC<{
route: NativeStackScreenProps<RootStackParamList, 'Details'>['route'];
navigation: NativeStackNavigationProp<RootStackParamList, 'Details'>;
}> = ({ route, navigation }) => {
const { itemId } = route.params;
const itemDetails = {
'1': { title: 'First Item', description: 'Detailed description of the first item', date: '2023-01-01' },
'2': { title: 'Second Item', description: 'Detailed description of the second item', date: '2023-01-02' },
'3': { title: 'Third Item', description: 'Detailed description of the third item', date: '2023-01-03' },
};
const item = itemDetails[itemId as keyof typeof itemDetails];
return (
<View style={styles.container}>
<Text style={styles.title}>Item Details</Text>
{item ? (
<View style={styles.detailsCard}>
<Text style={styles.detailsTitle}>{item.title}</Text>
<Text style={styles.detailsDescription}>{item.description}</Text>
<Text style={styles.detailsDate}>Created: {item.date}</Text>
<Text style={styles.itemIdText}>Item ID: {itemId}</Text>
</View>
) : (
<Text style={styles.notFound}>Item not found</Text>
)}
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={() => navigation.goBack()}
>
<Text style={styles.buttonText}>Go Back</Text>
</TouchableOpacity>
</View>
);
};
const SettingsScreen: React.FC<{
navigation: NativeStackNavigationProp<RootStackParamList, 'Settings'>;
}> = ({ navigation }) => {
const [notifications, setNotifications] = useState(true);
const [darkMode, setDarkMode] = useState(false);
return (
<View style={styles.container}>
<Text style={styles.title}>Settings</Text>
<View style={styles.settingsSection}>
<Text style={styles.sectionTitle}>Preferences</Text>
<TouchableOpacity
style={styles.settingItem}
onPress={() => setNotifications(!notifications)}
>
<Text style={styles.settingText}>Push Notifications</Text>
<View style={[styles.toggle, notifications && styles.toggleOn]}>
<Text style={styles.toggleText}>
{notifications ? 'ON' : 'OFF'}
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
style={styles.settingItem}
onPress={() => setDarkMode(!darkMode)}
>
<Text style={styles.settingText}>Dark Mode</Text>
<View style={[styles.toggle, darkMode && styles.toggleOn]}>
<Text style={styles.toggleText}>
{darkMode ? 'ON' : 'OFF'}
</Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={[styles.button, styles.dangerButton]}
onPress={() => navigation.goBack()}
>
<Text style={styles.buttonText}>Back</Text>
</TouchableOpacity>
</View>
</View>
);
};
const ModalScreen: React.FC<{
navigation: NativeStackNavigationProp<RootStackParamList, 'Modal'>;
}> = ({ navigation }) => {
return (
<View style={styles.modalContainer}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>Modal Screen</Text>
<Text style={styles.modalMessage}>This is a modal that can be dismissed.</Text>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={() => navigation.goBack()}
>
<Text style={styles.buttonText}>Close Modal</Text>
</TouchableOpacity>
</View>
</View>
);
};
// 2. Tab Navigation Example
const Tab = createBottomTabNavigator<TabParamList>();
const TabHomeScreen: React.FC = () => (
<View style={styles.container}>
<Text style={styles.title}>Tab Home</Text>
<Text style={styles.subtitle}>This is the home tab</Text>
</View>
);
const TabSearchScreen: React.FC = () => {
const [searchText, setSearchText] = useState('');
return (
<View style={styles.container}>
<Text style={styles.title}>Search</Text>
<TextInput
style={styles.searchInput}
placeholder="Search for something..."
value={searchText}
onChangeText={setSearchText}
/>
<Text style={styles.searchResult}>
{searchText ? `Searching for: "${searchText}"` : 'Enter search term'}
</Text>
</View>
);
};
const TabProfileScreen: React.FC = () => (
<View style={styles.container}>
<Text style={styles.title}>Tab Profile</Text>
<Text style={styles.subtitle}>This is the profile tab</Text>
</View>
);
const TabNavigator: React.FC = () => {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ color, size }) => {
let iconName = '';
if (route.name === 'HomeTab') {
iconName = '🏠';
} else if (route.name === 'SearchTab') {
iconName = '🔍';
} else if (route.name === 'ProfileTab') {
iconName = '👤';
}
return <Text style={{ fontSize: size, color }}>{iconName}</Text>;
},
tabBarActiveTintColor: '#3498db',
tabBarInactiveTintColor: '#95a5a6',
tabBarStyle: { backgroundColor: '#fff' },
})}
>
<Tab.Screen
name="HomeTab"
component={TabHomeScreen}
options={{ title: 'Home' }}
/>
<Tab.Screen
name="SearchTab"
component={TabSearchScreen}
options={{ title: 'Search' }}
/>
<Tab.Screen
name="ProfileTab"
component={TabProfileScreen}
options={{ title: 'Profile' }}
/>
</Tab.Navigator>
);
};
// 3. Complete App with Stack Navigation
const StackNavigator: React.FC = () => {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: { backgroundColor: '#3498db' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' },
}}
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'React Navigation Demo' }}
/>
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={({ route }) => ({ title: `Profile - ${route.params.userId}` })}
/>
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{ title: 'Item Details' }}
/>
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{ title: 'Settings' }}
/>
<Stack.Screen
name="Modal"
component={ModalScreen}
options={{
presentation: 'modal',
title: 'Modal',
headerShown: false,
}}
/>
</Stack.Navigator>
);
};
// 4. Main App Component
const NavigationApp: React.FC = () => {
const [useTabs, setUseTabs] = useState(false);
return (
<NavigationContainer>
{useTabs ? (
<View style={styles.container}>
<View style={styles.navigationToggle}>
<TouchableOpacity
style={[styles.toggleButton, useTabs && styles.activeToggle]}
onPress={() => setUseTabs(true)}
>
<Text style={styles.toggleButtonText}>Tabs</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.toggleButton, !useTabs && styles.activeToggle]}
onPress={() => setUseTabs(false)}
>
<Text style={styles.toggleButtonText}>Stack</Text>
</TouchableOpacity>
</View>
<TabNavigator />
</View>
) : (
<View style={styles.container}>
<View style={styles.navigationToggle}>
<TouchableOpacity
style={[styles.toggleButton, useTabs && styles.activeToggle]}
onPress={() => setUseTabs(true)}
>
<Text style={styles.toggleButtonText}>Tabs</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.toggleButton, !useTabs && styles.activeToggle]}
onPress={() => setUseTabs(false)}
>
<Text style={styles.toggleButtonText}>Stack</Text>
</TouchableOpacity>
</View>
<StackNavigator />
</View>
)}
</NavigationContainer>
);
};
// Styles
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8f9fa',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#2c3e50',
textAlign: 'center',
margin: 20,
},
subtitle: {
fontSize: 16,
color: '#7f8c8d',
textAlign: 'center',
marginBottom: 20,
},
itemList: {
flex: 1,
marginHorizontal: 20,
},
itemCard: {
backgroundColor: '#fff',
padding: 16,
marginBottom: 12,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
itemTitle: {
fontSize: 18,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 4,
},
itemDescription: {
fontSize: 14,
color: '#7f8c8d',
marginBottom: 8,
},
seeMore: {
fontSize: 12,
color: '#3498db',
textAlign: 'right',
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
padding: 20,
},
button: {
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
minWidth: 120,
},
primaryButton: {
backgroundColor: '#3498db',
},
secondaryButton: {
backgroundColor: '#95a5a6',
},
dangerButton: {
backgroundColor: '#e74c3c',
},
editButton: {
backgroundColor: '#f39c12',
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
profileHeader: {
alignItems: 'center',
marginBottom: 30,
},
avatar: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: '#3498db',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 16,
},
avatarText: {
fontSize: 24,
fontWeight: 'bold',
color: '#fff',
},
profileName: {
fontSize: 20,
fontWeight: 'bold',
color: '#2c3e50',
marginBottom: 4,
},
profileEmail: {
fontSize: 14,
color: '#7f8c8d',
},
profileContent: {
paddingHorizontal: 20,
},
sectionTitle: {
fontSize: 18,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 8,
},
bioText: {
fontSize: 16,
color: '#34495e',
marginBottom: 20,
},
userId: {
fontSize: 14,
color: '#7f8c8d',
marginBottom: 20,
},
navButtons: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 20,
marginTop: 20,
},
navButton: {
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 8,
},
backButton: {
backgroundColor: '#95a5a6',
},
settingsButton: {
backgroundColor: '#f39c12',
},
navButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
detailsCard: {
backgroundColor: '#fff',
padding: 20,
margin: 20,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
detailsTitle: {
fontSize: 20,
fontWeight: 'bold',
color: '#2c3e50',
marginBottom: 8,
},
detailsDescription: {
fontSize: 16,
color: '#34495e',
marginBottom: 12,
},
detailsDate: {
fontSize: 14,
color: '#7f8c8d',
marginBottom: 8,
},
itemIdText: {
fontSize: 12,
color: '#95a5a6',
},
notFound: {
fontSize: 16,
color: '#e74c3c',
textAlign: 'center',
margin: 20,
},
settingsSection: {
margin: 20,
},
settingItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 16,
borderBottomWidth: 1,
borderBottomColor: '#ecf0f1',
},
settingText: {
fontSize: 16,
color: '#2c3e50',
},
toggle: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 12,
backgroundColor: '#ecf0f1',
},
toggleOn: {
backgroundColor: '#27ae60',
},
toggleText: {
fontSize: 12,
fontWeight: '600',
color: '#7f8c8d',
},
toggleOnText: {
color: '#fff',
},
modalContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalContent: {
backgroundColor: '#fff',
padding: 30,
borderRadius: 12,
alignItems: 'center',
margin: 20,
},
modalTitle: {
fontSize: 20,
fontWeight: 'bold',
color: '#2c3e50',
marginBottom: 12,
},
modalMessage: {
fontSize: 16,
color: '#7f8c8d',
textAlign: 'center',
marginBottom: 20,
},
searchInput: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 12,
fontSize: 16,
margin: 20,
},
searchResult: {
fontSize: 16,
color: '#7f8c8d',
textAlign: 'center',
marginHorizontal: 20,
},
navigationToggle: {
flexDirection: 'row',
backgroundColor: '#34495e',
padding: 4,
},
toggleButton: {
flex: 1,
paddingVertical: 12,
alignItems: 'center',
borderRadius: 4,
},
activeToggle: {
backgroundColor: '#3498db',
},
toggleButtonText: {
color: '#ecf0f1',
fontSize: 14,
fontWeight: '500',
},
});
export default NavigationApp;
💻 Gestion d'État React Native typescript
Modèles de gestion d'état dans React Native incluant Redux, Context API et React Query
// React Native State Management Examples
import React, { useState, useContext, createContext, useReducer, useEffect } from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
TextInput,
ScrollView,
Alert,
ActivityIndicator
} from 'react-native';
// 1. useState and Local State Management
const LocalStateExample: React.FC = () => {
const [user, setUser] = useState({
name: '',
email: '',
age: '',
});
const [errors, setErrors] = useState<Record<string, string>>({});
const [isSubmitting, setIsSubmitting] = useState(false);
const validateForm = () => {
const newErrors: Record<string, string> = {};
if (!user.name.trim()) {
newErrors.name = 'Name is required';
}
if (!user.email.trim()) {
newErrors.email = 'Email is required';
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(user.email)) {
newErrors.email = 'Please enter a valid email';
}
if (!user.age.trim()) {
newErrors.age = 'Age is required';
} else if (parseInt(user.age) < 18) {
newErrors.age = 'You must be at least 18 years old';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async () => {
if (!validateForm()) return;
setIsSubmitting(true);
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 2000));
Alert.alert('Success', 'Form submitted successfully!');
setIsSubmitting(false);
};
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>Local State Management</Text>
<View style={styles.inputGroup}>
<Text style={styles.label}>Name</Text>
<TextInput
style={[styles.input, errors.name && styles.inputError]}
value={user.name}
onChangeText={(text) => setUser({ ...user, name: text })}
placeholder="Enter your name"
/>
{errors.name && <Text style={styles.errorText}>{errors.name}</Text>}
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Email</Text>
<TextInput
style={[styles.input, errors.email && styles.inputError]}
value={user.email}
onChangeText={(text) => setUser({ ...user, email: text })}
placeholder="Enter your email"
keyboardType="email-address"
/>
{errors.email && <Text style={styles.errorText}>{errors.email}</Text>}
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Age</Text>
<TextInput
style={[styles.input, errors.age && styles.inputError]}
value={user.age}
onChangeText={(text) => setUser({ ...user, age: text })}
placeholder="Enter your age"
keyboardType="numeric"
/>
{errors.age && <Text style={styles.errorText}>{errors.age}</Text>}
</View>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={handleSubmit}
disabled={isSubmitting}
>
{isSubmitting ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>Submit</Text>
)}
</TouchableOpacity>
<View style={styles.debugInfo}>
<Text style={styles.debugTitle}>Current State:</Text>
<Text style={styles.debugText}>{JSON.stringify(user, null, 2)}</Text>
</View>
</View>
);
};
// 2. Context API for Global State
interface AppState {
theme: 'light' | 'dark';
user: User | null;
notifications: Notification[];
}
interface User {
id: string;
name: string;
email: string;
}
interface Notification {
id: string;
message: string;
timestamp: Date;
}
interface AppContextType {
state: AppState;
setTheme: (theme: 'light' | 'dark') => void;
setUser: (user: User | null) => void;
addNotification: (message: string) => void;
clearNotifications: () => void;
}
const AppContext = createContext<AppContextType | null>(null);
export const useAppContext = () => {
const context = useContext(AppContext);
if (!context) {
throw new Error('useAppContext must be used within an AppProvider');
}
return context;
};
const AppProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [state, setState] = useState<AppState>({
theme: 'light',
user: null,
notifications: [],
});
const setTheme = (theme: 'light' | 'dark') => {
setState(prev => ({ ...prev, theme }));
};
const setUser = (user: User | null) => {
setState(prev => ({ ...prev, user }));
};
const addNotification = (message: string) => {
const notification: Notification = {
id: Date.now().toString(),
message,
timestamp: new Date(),
};
setState(prev => ({
...prev,
notifications: [notification, ...prev.notifications].slice(0, 5),
}));
};
const clearNotifications = () => {
setState(prev => ({ ...prev, notifications: [] }));
};
return (
<AppContext.Provider value={{
state,
setTheme,
setUser,
addNotification,
clearNotifications,
}}>
{children}
</AppContext.Provider>
);
};
const ThemeToggle: React.FC = () => {
const { state, setTheme } = useAppContext();
return (
<TouchableOpacity
style={[
styles.themeToggle,
state.theme === 'dark' && styles.themeToggleDark
]}
onPress={() => setTheme(state.theme === 'light' ? 'dark' : 'light')}
>
<Text style={styles.themeToggleText}>
{state.theme === 'light' ? '🌞' : '🌙'} {state.theme === 'light' ? 'Light' : 'Dark'}
</Text>
</TouchableOpacity>
);
};
const UserProfile: React.FC = () => {
const { state, setUser } = useAppContext();
const handleLogin = () => {
const user: User = {
id: '1',
name: 'John Doe',
email: '[email protected]',
};
setUser(user);
};
const handleLogout = () => {
setUser(null);
};
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>User Profile</Text>
{state.user ? (
<View>
<Text style={styles.userInfo}>Name: {state.user.name}</Text>
<Text style={styles.userInfo}>Email: {state.user.email}</Text>
<Text style={styles.userInfo}>ID: {state.user.id}</Text>
<TouchableOpacity
style={[styles.button, styles.dangerButton]}
onPress={handleLogout}
>
<Text style={styles.buttonText}>Logout</Text>
</TouchableOpacity>
</View>
) : (
<View>
<Text style={styles.placeholderText}>Not logged in</Text>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={handleLogin}
>
<Text style={styles.buttonText}>Login</Text>
</TouchableOpacity>
</View>
)}
</View>
);
};
const NotificationPanel: React.FC = () => {
const { state, addNotification, clearNotifications } = useAppContext();
const handleAddNotification = () => {
const messages = [
'New message received',
'Your profile was updated',
'System maintenance scheduled',
'New feature available',
];
const randomMessage = messages[Math.floor(Math.random() * messages.length)];
addNotification(randomMessage);
};
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>Notifications</Text>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={handleAddNotification}
>
<Text style={styles.buttonText}>Add Notification</Text>
</TouchableOpacity>
{state.notifications.length > 0 && (
<TouchableOpacity
style={[styles.button, styles.clearButton]}
onPress={clearNotifications}
>
<Text style={styles.buttonText}>Clear All</Text>
</TouchableOpacity>
)}
<ScrollView style={styles.notificationList}>
{state.notifications.map((notification) => (
<View key={notification.id} style={styles.notificationItem}>
<Text style={styles.notificationText}>{notification.message}</Text>
<Text style={styles.notificationTime}>
{notification.timestamp.toLocaleTimeString()}
</Text>
</View>
))}
</ScrollView>
{state.notifications.length === 0 && (
<Text style={styles.placeholderText}>No notifications</Text>
)}
</View>
);
};
// 3. useReducer for Complex State
interface Todo {
id: string;
text: string;
completed: boolean;
createdAt: Date;
}
type TodoAction =
| { type: 'ADD_TODO'; text: string }
| { type: 'TOGGLE_TODO'; id: string }
| { type: 'DELETE_TODO'; id: string }
| { type: 'EDIT_TODO'; id: string; text: string }
| { type: 'CLEAR_COMPLETED' };
const todoReducer = (state: Todo[], action: TodoAction): Todo[] => {
switch (action.type) {
case 'ADD_TODO':
return [
{
id: Date.now().toString(),
text: action.text,
completed: false,
createdAt: new Date(),
},
...state,
];
case 'TOGGLE_TODO':
return state.map((todo) =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
);
case 'DELETE_TODO':
return state.filter((todo) => todo.id !== action.id);
case 'EDIT_TODO':
return state.map((todo) =>
todo.id === action.id ? { ...todo, text: action.text } : todo
);
case 'CLEAR_COMPLETED':
return state.filter((todo) => !todo.completed);
default:
return state;
}
};
const TodoReducerExample: React.FC = () => {
const [todos, dispatch] = useReducer(todoReducer, []);
const [newTodoText, setNewTodoText] = useState('');
const addTodo = () => {
if (newTodoText.trim()) {
dispatch({ type: 'ADD_TODO', text: newTodoText.trim() });
setNewTodoText('');
}
};
const toggleTodo = (id: string) => {
dispatch({ type: 'TOGGLE_TODO', id });
};
const deleteTodo = (id: string) => {
Alert.alert(
'Delete Todo',
'Are you sure you want to delete this todo?',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Delete', style: 'destructive', onPress: () => dispatch({ type: 'DELETE_TODO', id }) }
]
);
};
const editTodo = (id: string, currentText: string) => {
Alert.prompt(
'Edit Todo',
'Edit your todo:',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Save',
onPress: (newText) => {
if (newText && newText.trim()) {
dispatch({ type: 'EDIT_TODO', id, text: newText.trim() });
}
},
},
],
'plain-text',
currentText
);
};
const completedCount = todos.filter(todo => todo.completed).length;
const activeCount = todos.filter(todo => !todo.completed).length;
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>useReducer Todo App</Text>
<View style={styles.inputGroup}>
<TextInput
style={styles.input}
value={newTodoText}
onChangeText={setNewTodoText}
placeholder="What needs to be done?"
onSubmitEditing={addTodo}
/>
<TouchableOpacity style={[styles.button, styles.addButton]} onPress={addTodo}>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
</View>
<View style={styles.statsContainer}>
<Text style={styles.statText}>
{activeCount} active, {completedCount} completed
</Text>
{completedCount > 0 && (
<TouchableOpacity
style={[styles.button, styles.clearButton]}
onPress={() => dispatch({ type: 'CLEAR_COMPLETED' })}
>
<Text style={styles.buttonTextSmall}>Clear Completed</Text>
</TouchableOpacity>
)}
</View>
<ScrollView style={styles.todoList}>
{todos.map((todo) => (
<View key={todo.id} style={styles.todoItem}>
<TouchableOpacity
style={styles.todoCheckbox}
onPress={() => toggleTodo(todo.id)}
>
<View style={[styles.checkbox, todo.completed && styles.checkboxChecked]}>
{todo.completed && <Text style={styles.checkmark}>✓</Text>}
</View>
</TouchableOpacity>
<View style={styles.todoContent}>
<TouchableOpacity
style={styles.todoTextContainer}
onPress={() => editTodo(todo.id, todo.text)}
>
<Text style={[styles.todoText, todo.completed && styles.todoTextCompleted]}>
{todo.text}
</Text>
</TouchableOpacity>
<Text style={styles.todoDate}>
{todo.createdAt.toLocaleDateString()}
</Text>
</View>
<TouchableOpacity
style={styles.deleteButton}
onPress={() => deleteTodo(todo.id)}
>
<Text style={styles.deleteButtonText}>×</Text>
</TouchableOpacity>
</View>
))}
</ScrollView>
{todos.length === 0 && (
<Text style={styles.placeholderText}>No todos yet. Add one above!</Text>
)}
<View style={styles.debugInfo}>
<Text style={styles.debugTitle}>State (useReducer):</Text>
<Text style={styles.debugText}>{JSON.stringify(todos.slice(0, 2), null, 2)}</Text>
{todos.length > 2 && <Text style={styles.debugText}>...and {todos.length - 2} more items</Text>}
</View>
</View>
);
};
// 4. Custom Hook for Data Fetching
interface UseDataResult<T> {
data: T | null;
loading: boolean;
error: string | null;
refetch: () => void;
}
function useData<T>(fetchFunction: () => Promise<T>): UseDataResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const result = await fetchFunction();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return { data, loading, error, refetch: fetchData };
}
// API simulation functions
const fetchUser = async (): Promise<User> => {
await new Promise(resolve => setTimeout(resolve, 1500));
return {
id: '1',
name: 'Jane Smith',
email: '[email protected]',
};
};
const fetchPosts = async (): Promise<Array<{ id: string; title: string; body: string }>> => {
await new Promise(resolve => setTimeout(resolve, 2000));
return [
{ id: '1', title: 'First Post', body: 'This is the first post' },
{ id: '2', title: 'Second Post', body: 'This is the second post' },
{ id: '3', title: 'Third Post', body: 'This is the third post' },
];
};
const DataFetchingExample: React.FC = () => {
const { data: user, loading: userLoading, error: userError, refetch: refetchUser } = useData(fetchUser);
const { data: posts, loading: postsLoading, error: postsError, refetch: refetchPosts } = useData(fetchPosts);
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>Custom Hook Data Fetching</Text>
<View style={styles.section}>
<Text style={styles.sectionTitle}>User Data</Text>
{userLoading && <ActivityIndicator color="#3498db" />}
{userError && <Text style={styles.errorText}>Error: {userError}</Text>}
{user && (
<View style={styles.dataDisplay}>
<Text style={styles.dataLabel}>Name: {user.name}</Text>
<Text style={styles.dataLabel}>Email: {user.email}</Text>
</View>
)}
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={refetchUser}
disabled={userLoading}
>
<Text style={styles.buttonText}>Refresh User</Text>
</TouchableOpacity>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Posts Data</Text>
{postsLoading && <ActivityIndicator color="#3498db" />}
{postsError && <Text style={styles.errorText}>Error: {postsError}</Text>}
{posts && (
<ScrollView style={styles.dataList}>
{posts.map((post) => (
<View key={post.id} style={styles.postItem}>
<Text style={styles.postTitle}>{post.title}</Text>
<Text style={styles.postBody}>{post.body}</Text>
</View>
))}
</ScrollView>
)}
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={refetchPosts}
disabled={postsLoading}
>
<Text style={styles.buttonText}>Refresh Posts</Text>
</TouchableOpacity>
</View>
</View>
);
};
// 5. Main App Component with Context Provider
const StateManagementApp: React.FC = () => {
return (
<AppProvider>
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>State Management Examples</Text>
<ThemeToggle />
</View>
<LocalStateExample />
<UserProfile />
<NotificationPanel />
<TodoReducerExample />
<DataFetchingExample />
</ScrollView>
</AppProvider>
);
};
// Styles
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8f9fa',
},
header: {
padding: 20,
backgroundColor: '#3498db',
alignItems: 'center',
},
headerTitle: {
fontSize: 24,
fontWeight: 'bold',
color: '#fff',
marginBottom: 16,
textAlign: 'center',
},
themeToggle: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#ecf0f1',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 20,
},
themeToggleDark: {
backgroundColor: '#34495e',
},
themeToggleText: {
fontSize: 16,
fontWeight: '500',
},
card: {
backgroundColor: '#fff',
margin: 16,
padding: 20,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
cardTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#2c3e50',
marginBottom: 16,
textAlign: 'center',
},
inputGroup: {
marginBottom: 16,
},
label: {
fontSize: 16,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 8,
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 6,
padding: 12,
fontSize: 16,
backgroundColor: '#fff',
},
inputError: {
borderColor: '#e74c3c',
},
errorText: {
color: '#e74c3c',
fontSize: 14,
marginTop: 4,
},
button: {
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 6,
alignItems: 'center',
marginTop: 8,
},
primaryButton: {
backgroundColor: '#3498db',
},
secondaryButton: {
backgroundColor: '#95a5a6',
},
dangerButton: {
backgroundColor: '#e74c3c',
},
clearButton: {
backgroundColor: '#f39c12',
},
addButton: {
backgroundColor: '#27ae60',
width: 48,
height: 48,
borderRadius: 6,
justifyContent: 'center',
alignItems: 'center',
marginLeft: 8,
},
addButtonText: {
color: '#fff',
fontSize: 24,
fontWeight: 'bold',
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
buttonTextSmall: {
color: '#fff',
fontSize: 14,
fontWeight: '600',
},
userInfo: {
fontSize: 16,
color: '#2c3e50',
marginBottom: 8,
},
placeholderText: {
fontSize: 16,
color: '#7f8c8d',
textAlign: 'center',
marginVertical: 16,
},
debugInfo: {
marginTop: 20,
padding: 12,
backgroundColor: '#2c3e50',
borderRadius: 6,
},
debugTitle: {
fontSize: 14,
fontWeight: '600',
color: '#ecf0f1',
marginBottom: 8,
},
debugText: {
fontSize: 12,
color: '#bdc3c7',
fontFamily: 'monospace',
},
notificationList: {
maxHeight: 200,
marginTop: 16,
},
notificationItem: {
backgroundColor: '#ecf0f1',
padding: 12,
marginBottom: 8,
borderRadius: 6,
borderLeftWidth: 4,
borderLeftColor: '#3498db',
},
notificationText: {
fontSize: 14,
color: '#2c3e50',
marginBottom: 4,
},
notificationTime: {
fontSize: 12,
color: '#7f8c8d',
},
statsContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 8,
marginBottom: 16,
},
statText: {
fontSize: 14,
color: '#7f8c8d',
},
todoList: {
maxHeight: 300,
},
todoItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 8,
borderBottomWidth: 1,
borderBottomColor: '#ecf0f1',
},
todoCheckbox: {
marginRight: 12,
},
checkbox: {
width: 24,
height: 24,
borderWidth: 2,
borderColor: '#ddd',
borderRadius: 4,
justifyContent: 'center',
alignItems: 'center',
},
checkboxChecked: {
backgroundColor: '#27ae60',
borderColor: '#27ae60',
},
checkmark: {
color: '#fff',
fontSize: 16,
fontWeight: 'bold',
},
todoContent: {
flex: 1,
},
todoTextContainer: {
flex: 1,
},
todoText: {
fontSize: 16,
color: '#2c3e50',
},
todoTextCompleted: {
textDecorationLine: 'line-through',
color: '#7f8c8d',
},
todoDate: {
fontSize: 12,
color: '#95a5a6',
marginTop: 2,
},
deleteButton: {
padding: 8,
backgroundColor: '#e74c3c',
borderRadius: 4,
marginLeft: 8,
},
deleteButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: 'bold',
},
section: {
marginBottom: 24,
},
sectionTitle: {
fontSize: 16,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 12,
},
dataDisplay: {
backgroundColor: '#f8f9fa',
padding: 12,
borderRadius: 6,
marginBottom: 12,
},
dataLabel: {
fontSize: 16,
color: '#2c3e50',
marginBottom: 4,
},
dataList: {
maxHeight: 200,
marginBottom: 12,
},
postItem: {
backgroundColor: '#f8f9fa',
padding: 12,
marginBottom: 8,
borderRadius: 6,
},
postTitle: {
fontSize: 16,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 4,
},
postBody: {
fontSize: 14,
color: '#7f8c8d',
},
});
export default StateManagementApp;
💻 Intégration Native React Native typescript
Intégration de modules natifs, code spécifique à plateforme et pontage de React Native avec les APIs natives
// React Native Native Integration Examples
import React, { useState, useEffect, useRef } from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
ScrollView,
Alert,
NativeModules,
Platform,
PermissionsAndroid,
Linking,
Share,
Dimensions,
AppState,
DeviceEventEmitter
} from 'react-native';
import { CameraRoll } from '@react-native-community/cameraroll';
import Geolocation from '@react-native-community/geolocation';
// 1. Platform-specific code
const PlatformSpecificExample: React.FC = () => {
const [platformInfo, setPlatformInfo] = useState({});
useEffect(() => {
setPlatformInfo({
os: Platform.OS,
version: Platform.Version,
isPad: Platform.isPad,
isTVOS: Platform.isTVOS,
constants: Platform.constants,
});
}, []);
const handlePlatformAction = () => {
if (Platform.OS === 'ios') {
Alert.alert('iOS Feature', 'This is an iOS-specific feature');
} else if (Platform.OS === 'android') {
Alert.alert('Android Feature', 'This is an Android-specific feature');
}
};
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>Platform-Specific Code</Text>
<View style={styles.platformInfo}>
<Text style={styles.infoLabel}>Platform: {Platform.OS}</Text>
<Text style={styles.infoLabel}>Version: {Platform.Version}</Text>
{Platform.OS === 'ios' && (
<>
<Text style={styles.infoLabel}>Is iPad: {Platform.isPad ? 'Yes' : 'No'}</Text>
<Text style={styles.infoLabel}>Is TV: {Platform.isTVOS ? 'Yes' : 'No'}</Text>
</>
)}
</View>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={handlePlatformAction}
>
<Text style={styles.buttonText}>
{Platform.OS === 'ios' ? 'iOS Action' : 'Android Action'}
</Text>
</TouchableOpacity>
<View style={styles.platformSpecificUI}>
{Platform.OS === 'ios' ? (
<View style={styles.iosUI}>
<Text style={styles.iosLabel}>iOS UI Elements</Text>
<View style={styles.iosButton}>
<Text style={styles.iosButtonText}>iOS Button</Text>
</View>
</View>
) : (
<View style={styles.androidUI}>
<Text style={styles.androidLabel}>Android UI Elements</Text>
<View style={styles.androidButton}>
<Text style={styles.androidButtonText}>Android Button</Text>
</View>
</View>
)}
</View>
</View>
);
};
// 2. Device APIs Integration
const DeviceAPIsExample: React.FC = () => {
const [location, setLocation] = useState(null);
const [dimensions, setDimensions] = useState(Dimensions.get('window'));
const [appState, setAppState] = useState(AppState.currentState);
const [shareContent, setShareContent] = useState('Check out this React Native app!');
useEffect(() => {
const dimensionHandler = ({ window }) => setDimensions(window);
const subscription = Dimensions.addEventListener('change', dimensionHandler);
const appStateHandler = (nextAppState) => setAppState(nextAppState);
const appStateSubscription = AppState.addEventListener('change', appStateHandler);
return () => {
subscription?.remove();
appStateSubscription?.remove();
};
}, []);
const getCurrentLocation = () => {
Geolocation.getCurrentPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
});
},
(error) => {
Alert.alert('Error', 'Unable to get location');
},
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
);
};
const watchLocation = () => {
const watchId = Geolocation.watchPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
});
},
(error) => {
Alert.alert('Error', 'Unable to watch location');
},
{ enableHighAccuracy: true, distanceFilter: 10 }
);
Alert.alert('Watching Location', 'Location updates started');
};
const shareContentAction = async () => {
try {
await Share.share({
message: shareContent,
url: 'https://reactnative.dev',
});
} catch (error) {
Alert.alert('Error', 'Unable to share content');
}
};
const openExternalApp = async () => {
const url = 'https://reactnative.dev';
const supported = await Linking.canOpenURL(url);
if (supported) {
await Linking.openURL(url);
} else {
Alert.alert('Error', 'Unable to open URL');
}
};
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>Device APIs Integration</Text>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Screen Dimensions</Text>
<Text style={styles.infoLabel}>Width: {dimensions.width}</Text>
<Text style={styles.infoLabel}>Height: {dimensions.height}</Text>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>App State: {appState}</Text>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Location Services</Text>
{location ? (
<View style={styles.locationInfo}>
<Text style={styles.infoLabel}>Latitude: {location.latitude}</Text>
<Text style={styles.infoLabel}>Longitude: {location.longitude}</Text>
{location.accuracy && (
<Text style={styles.infoLabel}>Accuracy: {location.accuracy}m</Text>
)}
</View>
) : (
<Text style={styles.placeholderText}>No location data</Text>
)}
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={getCurrentLocation}
>
<Text style={styles.buttonText}>Get Current Location</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={watchLocation}
>
<Text style={styles.buttonText}>Watch Location</Text>
</TouchableOpacity>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>External Integration</Text>
<View style={styles.shareContainer}>
<TextInput
style={styles.shareInput}
value={shareContent}
onChangeText={setShareContent}
placeholder="Enter content to share"
/>
<TouchableOpacity
style={[styles.button, styles.shareButton]}
onPress={shareContentAction}
>
<Text style={styles.buttonText}>Share</Text>
</TouchableOpacity>
</View>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={openExternalApp}
>
<Text style={styles.buttonText}>Open React Native Website</Text>
</TouchableOpacity>
</View>
</View>
);
};
// 3. Permissions Management
const PermissionsExample: React.FC = () => {
const [permissions, setPermissions] = useState({});
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',
}
);
setPermissions(prev => ({
...prev,
location: granted === PermissionsAndroid.RESULTS.GRANTED,
}));
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
Alert.alert('Success', 'Location permission granted');
} else {
Alert.alert('Error', 'Location permission denied');
}
} catch (err) {
Alert.alert('Error', 'Unable to request location permission');
}
} else {
Alert.alert('Info', 'Location permissions are handled differently on iOS');
}
};
const requestCameraPermission = async () => {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Camera Permission',
message: 'This app needs access to your camera',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
}
);
setPermissions(prev => ({
...prev,
camera: granted === PermissionsAndroid.RESULTS.GRANTED,
}));
Alert.alert(
'Camera Permission',
granted === PermissionsAndroid.RESULTS.GRANTED ? 'Granted' : 'Denied'
);
} catch (err) {
Alert.alert('Error', 'Unable to request camera permission');
}
} else {
Alert.alert('Info', 'Camera permissions are handled differently on iOS');
}
};
const checkPermission = async (permission: string) => {
if (Platform.OS === 'android') {
try {
const hasPermission = await PermissionsAndroid.check(permission);
Alert.alert('Permission Status', hasPermission ? 'Granted' : 'Not Granted');
} catch (err) {
Alert.alert('Error', 'Unable to check permission');
}
}
};
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>Permissions Management</Text>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Location Permission</Text>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={requestLocationPermission}
>
<Text style={styles.buttonText}>Request Location Permission</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={() => checkPermission(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION)}
>
<Text style={styles.buttonText}>Check Location Permission</Text>
</TouchableOpacity>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Camera Permission</Text>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={requestCameraPermission}
>
<Text style={styles.buttonText}>Request Camera Permission</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={() => checkPermission(PermissionsAndroid.PERMISSIONS.CAMERA)}
>
<Text style={styles.buttonText}>Check Camera Permission</Text>
</TouchableOpacity>
</View>
<View style={styles.permissionsStatus}>
<Text style={styles.sectionTitle}>Current Permissions Status</Text>
{Object.entries(permissions).map(([key, value]) => (
<Text key={key} style={styles.infoLabel}>
{key}: {value ? '✓ Granted' : '✗ Denied'}
</Text>
))}
</View>
</View>
);
};
// 4. Native Module Example (Simulation)
const NativeModuleExample: React.FC = () => {
const [batteryLevel, setBatteryLevel] = useState(null);
const [deviceInfo, setDeviceInfo] = useState({});
// Simulating native module calls
const getBatteryLevel = async () => {
// This would normally call a native module
// For example: const level = await NativeModules.Battery.getLevel();
// Simulate battery level
const simulatedLevel = Math.random() * 100;
setBatteryLevel(simulatedLevel.toFixed(1));
Alert.alert('Battery Level', `Current battery: ${simulatedLevel.toFixed(1)}%`);
};
const getDeviceInfo = async () => {
// This would normally call a native module
// For example: const info = await NativeModules.DeviceInfo.getInfo();
const simulatedInfo = {
model: Platform.OS === 'ios' ? 'iPhone Simulator' : 'Android Emulator',
brand: Platform.OS === 'ios' ? 'Apple' : 'Google',
systemVersion: Platform.Version,
};
setDeviceInfo(simulatedInfo);
};
const showAlert = (title: string, message: string) => {
// This would normally call a native module for custom alerts
Alert.alert(title, message);
};
const vibrateDevice = () => {
// This would normally call a native module
// For example: NativeModules.Vibration.vibrate(1000);
Alert.alert('Vibration', 'Device would vibrate for 1 second');
};
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>Native Module Integration</Text>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Battery Information</Text>
{batteryLevel !== null && (
<Text style={styles.infoLabel}>
Battery Level: {batteryLevel}%
</Text>
)}
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={getBatteryLevel}
>
<Text style={styles.buttonText}>Get Battery Level</Text>
</TouchableOpacity>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Device Information</Text>
{Object.keys(deviceInfo).length > 0 ? (
<View style={styles.deviceInfo}>
<Text style={styles.infoLabel}>Model: {deviceInfo.model}</Text>
<Text style={styles.infoLabel}>Brand: {deviceInfo.brand}</Text>
<Text style={styles.infoLabel}>OS Version: {deviceInfo.systemVersion}</Text>
</View>
) : (
<Text style={styles.placeholderText}>No device info available</Text>
)}
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={getDeviceInfo}
>
<Text style={styles.buttonText}>Get Device Info</Text>
</TouchableOpacity>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Native Actions</Text>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={() => showAlert('Custom Alert', 'This is a custom native alert')}
>
<Text style={styles.buttonText}>Show Native Alert</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={vibrateDevice}
>
<Text style={styles.buttonText}>Vibrate Device</Text>
</TouchableOpacity>
</View>
<View style={styles.nativeModuleInfo}>
<Text style={styles.infoLabel}>
Note: These examples simulate native module calls.
In a real app, you would create custom native modules
for iOS (Swift/Objective-C) and Android (Java/Kotlin).
</Text>
</View>
</View>
);
};
// 5. Event Emitter and Device Events
const EventEmitterExample: React.FC = () => {
const [events, setEvents] = useState([]);
const [isListening, setIsListening] = useState(false);
const eventListenerRef = useRef(null);
const addEvent = (eventName: string, data: any) => {
const newEvent = {
id: Date.now(),
name: eventName,
data: JSON.stringify(data),
timestamp: new Date().toLocaleTimeString(),
};
setEvents(prev => [newEvent, ...prev].slice(0, 10));
};
const startListening = () => {
if (!isListening) {
// Listen to app state changes
eventListenerRef.current = AppState.addEventListener('change', (nextAppState) => {
addEvent('App State Change', { state: nextAppState });
});
// Listen to memory warnings (iOS only)
if (Platform.OS === 'ios') {
// This would normally be implemented as a native module
console.log('Memory warning listener would be set up here');
}
setIsListening(true);
addEvent('Event Listener Started', { status: 'active' });
}
};
const stopListening = () => {
if (eventListenerRef.current) {
eventListenerRef.current.remove();
eventListenerRef.current = null;
}
setIsListening(false);
addEvent('Event Listener Stopped', { status: 'inactive' });
};
const emitCustomEvent = () => {
// Emit a custom event
DeviceEventEmitter.emit('customEvent', {
message: 'Hello from custom event!',
timestamp: Date.now(),
});
addEvent('Custom Event Emitted', { type: 'customEvent' });
};
const simulateMemoryWarning = () => {
if (Platform.OS === 'ios') {
// Simulate memory warning event
addEvent('Memory Warning', { level: 'high' });
} else {
addEvent('Low Memory', { available: '45MB' });
}
};
useEffect(() => {
// Listen for custom events
const customEventSubscription = DeviceEventEmitter.addListener('customEvent', (event) => {
addEvent('Custom Event Received', event);
});
return () => {
customEventSubscription.remove();
};
}, []);
return (
<View style={styles.card}>
<Text style={styles.cardTitle}>Event Emitter & Device Events</Text>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Event Listener Status</Text>
<View style={styles.statusContainer}>
<Text style={styles.statusText}>
Status: {isListening ? '🟢 Listening' : '🔴 Not Listening'}
</Text>
<TouchableOpacity
style={[
styles.button,
isListening ? styles.dangerButton : styles.primaryButton
]}
onPress={isListening ? stopListening : startListening}
>
<Text style={styles.buttonText}>
{isListening ? 'Stop Listening' : 'Start Listening'}
</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Event Actions</Text>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={emitCustomEvent}
>
<Text style={styles.buttonText}>Emit Custom Event</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={simulateMemoryWarning}
>
<Text style={styles.buttonText}>
{Platform.OS === 'ios' ? 'Simulate Memory Warning' : 'Simulate Low Memory'}
</Text>
</TouchableOpacity>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Event Log</Text>
<ScrollView style={styles.eventLog}>
{events.map((event) => (
<View key={event.id} style={styles.eventItem}>
<Text style={styles.eventName}>{event.name}</Text>
<Text style={styles.eventData}>{event.data}</Text>
<Text style={styles.eventTime}>{event.timestamp}</Text>
</View>
))}
{events.length === 0 && (
<Text style={styles.placeholderText}>No events logged yet</Text>
)}
</ScrollView>
</View>
</View>
);
};
// Main App Component
const NativeIntegrationApp: React.FC = () => {
return (
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>React Native Integration</Text>
<Text style={styles.headerSubtitle}>Platform-specific features and native APIs</Text>
</View>
<PlatformSpecificExample />
<DeviceAPIsExample />
<PermissionsExample />
<NativeModuleExample />
<EventEmitterExample />
</ScrollView>
);
};
// Styles
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8f9fa',
},
header: {
padding: 20,
backgroundColor: '#2c3e50',
alignItems: 'center',
},
headerTitle: {
fontSize: 24,
fontWeight: 'bold',
color: '#fff',
marginBottom: 8,
},
headerSubtitle: {
fontSize: 16,
color: '#ecf0f1',
textAlign: 'center',
},
card: {
backgroundColor: '#fff',
margin: 16,
padding: 20,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
cardTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#2c3e50',
marginBottom: 16,
textAlign: 'center',
},
section: {
marginBottom: 20,
},
sectionTitle: {
fontSize: 16,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 12,
},
platformInfo: {
backgroundColor: '#ecf0f1',
padding: 12,
borderRadius: 6,
marginBottom: 16,
},
infoLabel: {
fontSize: 14,
color: '#2c3e50',
marginBottom: 4,
},
button: {
paddingVertical: 12,
paddingHorizontal: 20,
borderRadius: 6,
alignItems: 'center',
marginVertical: 4,
},
primaryButton: {
backgroundColor: '#3498db',
},
secondaryButton: {
backgroundColor: '#95a5a6',
},
dangerButton: {
backgroundColor: '#e74c3c',
},
shareButton: {
backgroundColor: '#27ae60',
marginLeft: 8,
flex: 1,
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
platformSpecificUI: {
marginTop: 16,
},
iosUI: {
backgroundColor: '#f8f9fa',
padding: 16,
borderRadius: 8,
borderWidth: 1,
borderColor: '#ddd',
},
iosLabel: {
fontSize: 16,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 12,
textAlign: 'center',
},
iosButton: {
backgroundColor: '#007AFF',
padding: 12,
borderRadius: 8,
alignItems: 'center',
},
iosButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
androidUI: {
backgroundColor: '#f8f9fa',
padding: 16,
borderRadius: 8,
borderWidth: 1,
borderColor: '#ddd',
},
androidLabel: {
fontSize: 16,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 12,
textAlign: 'center',
},
androidButton: {
backgroundColor: '#4CAF50',
padding: 12,
borderRadius: 4,
alignItems: 'center',
elevation: 2,
},
androidButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
locationInfo: {
backgroundColor: '#ecf0f1',
padding: 12,
borderRadius: 6,
marginBottom: 12,
},
shareContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
shareInput: {
flex: 1,
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 6,
padding: 12,
fontSize: 16,
marginRight: 8,
},
placeholderText: {
fontSize: 14,
color: '#7f8c8d',
fontStyle: 'italic',
textAlign: 'center',
marginVertical: 8,
},
statusContainer: {
alignItems: 'center',
marginBottom: 16,
},
statusText: {
fontSize: 16,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 12,
},
permissionsStatus: {
backgroundColor: '#f8f9fa',
padding: 12,
borderRadius: 6,
},
deviceInfo: {
backgroundColor: '#ecf0f1',
padding: 12,
borderRadius: 6,
},
nativeModuleInfo: {
backgroundColor: '#fff3cd',
border: 1,
borderColor: '#ffeaa7',
borderRadius: 6,
padding: 12,
marginTop: 16,
},
eventLog: {
maxHeight: 200,
backgroundColor: '#f8f9fa',
borderRadius: 6,
},
eventItem: {
backgroundColor: '#fff',
padding: 12,
marginBottom: 4,
borderRadius: 4,
borderLeftWidth: 3,
borderLeftColor: '#3498db',
},
eventName: {
fontSize: 14,
fontWeight: '600',
color: '#2c3e50',
marginBottom: 2,
},
eventData: {
fontSize: 12,
color: '#7f8c8d',
marginBottom: 2,
},
eventTime: {
fontSize: 10,
color: '#95a5a6',
},
});
export default NativeIntegrationApp;