React Native Cheatsheet
React Native - Lernen Sie einmalig, Schreiben Sie Anywhere
React Native ist ein Rahmen für den Aufbau nativer mobiler Anwendungen mit React. Es ermöglicht Entwicklern, React zusammen mit nativen Plattform-Funktionen zu verwenden, um mobile Apps für iOS und Android mit einer einzigen Codebase zu erstellen. < p>
Inhaltsverzeichnis
- [Installation](LINK_0 -%20(LINK_0)
- Core Components
- [Navigation](_LINK_0 -%20[State%20Management](_LINK_0 -%20(Styling)(_LINK_0__)
- APIs und Services
- Native Module
- [Performance](LINK_0 -%20Test
- [Debugging](_LINK_0__ -%20[Bestellung](_LINK_0 -%20[Platform-Specific%20Code](_LINK_0 -%20[Drittparteienbibliotheken](LINK_0 -%20Beste%20Praktiken
- (__LINK_0___)
Installation
Entwicklung Umwelt Setup
Voraussetzungen
# Install Node.js (LTS version recommended)
# Download from https://nodejs.org/
# Install Watchman (macOS)
brew install watchman
# Install Java Development Kit (JDK 11)
# Download from https://adoptopenjdk.net/
# Install Android Studio
# Download from https://developer.android.com/studio
# Install Xcode (macOS only)
# Download from Mac App Store
```_
#### React Native CLI
```bash
# Install React Native CLI globally
npm install -g react-native-cli
# Alternative: Use npx (recommended)
npx react-native --version
# Install Expo CLI (for Expo workflow)
npm install -g expo-cli
```_
#### Android Development Setup
```bash
# Set environment variables (add to ~/.bashrc or ~/.zshrc)
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
# Create Android Virtual Device (AVD)
# Open Android Studio > AVD Manager > Create Virtual Device
# Start Android emulator
emulator -avd Pixel_4_API_30
```_
#### iOS Development Setup (nur macOS)
```bash
# Install CocoaPods
sudo gem install cocoapods
# Install iOS Simulator
# Included with Xcode
# Install iOS dependencies
cd ios && pod install && cd ..
```_
### Projekterstellung
#### React Native CLI
```bash
# Create new project
npx react-native init MyApp
# Create project with specific template
npx react-native init MyApp --template react-native-template-typescript
# Navigate to project
cd MyApp
# Install dependencies
npm install
# Install iOS dependencies (macOS only)
cd ios && pod install && cd ..
```_
#### Risikopositionen
```bash
# Create new Expo project
expo init MyExpoApp
# Choose template
# - blank: minimal app
# - blank (TypeScript): minimal TypeScript app
# - tabs: app with tab navigation
# - minimal: bare minimum setup
# Navigate to project
cd MyExpoApp
# Start development server
expo start
```_
### Ausführen der App
#### React Native CLI
```bash
# Run on Android
npx react-native run-android
# Run on iOS (macOS only)
npx react-native run-ios
# Run on specific device
npx react-native run-ios --device "iPhone 12"
# Run on specific simulator
npx react-native run-ios --simulator="iPhone 12 Pro Max"
# Start Metro bundler separately
npx react-native start
# Reset cache
npx react-native start --reset-cache
```_
#### Exposition
```bash
# Start development server
expo start
# Run on Android device/emulator
expo start --android
# Run on iOS device/simulator
expo start --ios
# Run in web browser
expo start --web
# Run in tunnel mode (for physical devices)
expo start --tunnel
```_
## Erste Schritte
### Grundlegende App Struktur
```javascript
// App.js
import React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
View,
} from 'react-native';
const App = () => {
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" />
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View style={styles.body}>
<Text style={styles.title}>Welcome to React Native!</Text>
<Text style={styles.subtitle}>
Edit App.js to change this screen and then come back to see your edits.
</Text>
</View>
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
body: {
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
},
subtitle: {
fontSize: 16,
textAlign: 'center',
color: '#666',
},
});
export default App;
```_
### TypScript Setup
```bash
# Add TypeScript to existing project
npm install --save-dev typescript @types/react @types/react-native
# Create tsconfig.json
npx tsc --init
# Rename App.js to App.tsx
mv App.js App.tsx
```_
```json
// tsconfig.json
{
"compilerOptions": {
"target": "es2017",
"lib": ["es2017", "es7", "es6"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-native"
},
"include": [
"src/**/*",
"App.tsx"
],
"exclude": [
"node_modules"
]
}
```_
### Projektstruktur
MyApp/ ├── android/ # Android-specific code ├── ios/ # iOS-specific code ├── src/ # Source code │ ├── components/ # Reusable components │ ├── screens/ # Screen components │ ├── navigation/ # Navigation configuration │ ├── services/ # API services │ ├── utils/ # Utility functions │ ├── hooks/ # Custom hooks │ ├── context/ # React context │ └── types/ # TypeScript types ├── tests/ # Test files ├── App.js # Main app component ├── index.js # App entry point ├── package.json # Dependencies └── metro.config.js # Metro bundler config
## Kernkomponenten
### Grundkomponenten
```javascript
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
Image,
ScrollView,
FlatList,
Alert,
} from 'react-native';
const BasicComponents = () => {
const [text, setText] = useState('');
const [items] = useState([
{ id: '1', title: 'Item 1' },
{ id: '2', title: 'Item 2' },
{ id: '3', title: 'Item 3' },
]);
const handlePress = () => {
Alert.alert('Button Pressed', `You entered: ${text}`);
};
const renderItem = ({ item }) => (
<View style={styles.listItem}>
<Text>{item.title}</Text>
</View>
);
return (
<ScrollView style={styles.container}>
{/* Text Component */}
<Text style={styles.heading}>React Native Components</Text>
{/* TextInput Component */}
<TextInput
style={styles.input}
placeholder="Enter text here"
value={text}
onChangeText={setText}
multiline={false}
autoCapitalize="none"
autoCorrect={false}
/>
{/* TouchableOpacity Component */}
<TouchableOpacity style={styles.button} onPress={handlePress}>
<Text style={styles.buttonText}>Press Me</Text>
</TouchableOpacity>
{/* Image Component */}
<Image
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
style={styles.image}
resizeMode="contain"
/>
{/* Local Image */}
<Image
source={require('./assets/logo.png')}
style={styles.image}
/>
{/* FlatList Component */}
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={item => item.id}
style={styles.list}
/>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
heading: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
input: {
borderWidth: 1,
borderColor: '#ddd',
padding: 10,
marginBottom: 20,
borderRadius: 5,
},
button: {
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 5,
alignItems: 'center',
marginBottom: 20,
},
buttonText: {
color: 'white',
fontWeight: 'bold',
},
image: {
width: 100,
height: 100,
marginBottom: 20,
},
list: {
maxHeight: 200,
},
listItem: {
padding: 10,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
});
```_
### Erweiterte Komponenten
```javascript
import React, { useState, useRef } from 'react';
import {
View,
Text,
Modal,
Switch,
Slider,
Picker,
ActivityIndicator,
RefreshControl,
Animated,
PanGestureHandler,
} from 'react-native';
const AdvancedComponents = () => {
const [modalVisible, setModalVisible] = useState(false);
const [switchValue, setSwitchValue] = useState(false);
const [sliderValue, setSliderValue] = useState(50);
const [pickerValue, setPickerValue] = useState('option1');
const [refreshing, setRefreshing] = useState(false);
const animatedValue = useRef(new Animated.Value(0)).current;
const onRefresh = () => {
setRefreshing(true);
setTimeout(() => setRefreshing(false), 2000);
};
const startAnimation = () => {
Animated.timing(animatedValue, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
};
return (
<ScrollView
style={styles.container}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
>
{/* Modal Component */}
<TouchableOpacity
style={styles.button}
onPress={() => setModalVisible(true)}
>
<Text style={styles.buttonText}>Show Modal</Text>
</TouchableOpacity>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}
>
<View style={styles.modalContainer}>
<View style={styles.modalContent}>
<Text style={styles.modalText}>This is a modal!</Text>
<TouchableOpacity
style={styles.closeButton}
onPress={() => setModalVisible(false)}
>
<Text style={styles.buttonText}>Close</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
{/* Switch Component */}
<View style={styles.row}>
<Text>Enable notifications:</Text>
<Switch
value={switchValue}
onValueChange={setSwitchValue}
trackColor={{ false: '#767577', true: '#81b0ff' }}
thumbColor={switchValue ? '#f5dd4b' : '#f4f3f4'}
/>
</View>
{/* Slider Component */}
<View style={styles.sliderContainer}>
<Text>Value: {Math.round(sliderValue)}</Text>
<Slider
style={styles.slider}
minimumValue={0}
maximumValue={100}
value={sliderValue}
onValueChange={setSliderValue}
minimumTrackTintColor="#1fb28a"
maximumTrackTintColor="#d3d3d3"
thumbStyle={{ backgroundColor: '#1fb28a' }}
/>
</View>
{/* Picker Component */}
<View style={styles.pickerContainer}>
<Text>Select an option:</Text>
<Picker
selectedValue={pickerValue}
style={styles.picker}
onValueChange={setPickerValue}
>
<Picker.Item label="Option 1" value="option1" />
<Picker.Item label="Option 2" value="option2" />
<Picker.Item label="Option 3" value="option3" />
</Picker>
</View>
{/* Activity Indicator */}
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#0000ff" />
<Text>Loading...</Text>
</View>
{/* Animated Component */}
<TouchableOpacity style={styles.button} onPress={startAnimation}>
<Text style={styles.buttonText}>Start Animation</Text>
</TouchableOpacity>
<Animated.View
style={[
styles.animatedBox,
{
opacity: animatedValue,
transform: [
{
translateY: animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 100],
}),
},
],
},
]}
>
<Text>Animated Box</Text>
</Animated.View>
</ScrollView>
);
};
```_
### Benutzerdefinierte Komponenten
```javascript
// components/CustomButton.js
import React from 'react';
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
const CustomButton = ({
title,
onPress,
style,
textStyle,
disabled = false,
variant = 'primary'
}) => {
const buttonStyle = [
styles.button,
styles[variant],
disabled && styles.disabled,
style,
];
const buttonTextStyle = [
styles.buttonText,
styles[`${variant}Text`],
disabled && styles.disabledText,
textStyle,
];
return (
<TouchableOpacity
style={buttonStyle}
onPress={onPress}
disabled={disabled}
activeOpacity={0.7}
>
<Text style={buttonTextStyle}>{title}</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
button: {
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
minHeight: 44,
},
buttonText: {
fontSize: 16,
fontWeight: '600',
},
primary: {
backgroundColor: '#007AFF',
},
primaryText: {
color: '#FFFFFF',
},
secondary: {
backgroundColor: '#F2F2F7',
borderWidth: 1,
borderColor: '#C7C7CC',
},
secondaryText: {
color: '#007AFF',
},
danger: {
backgroundColor: '#FF3B30',
},
dangerText: {
color: '#FFFFFF',
},
disabled: {
backgroundColor: '#C7C7CC',
},
disabledText: {
color: '#8E8E93',
},
});
export default CustomButton;
// Usage
import CustomButton from './components/CustomButton';
const App = () => {
return (
<View style={styles.container}>
<CustomButton
title="Primary Button"
onPress={() => console.log('Primary pressed')}
variant="primary"
/>
<CustomButton
title="Secondary Button"
onPress={() => console.log('Secondary pressed')}
variant="secondary"
style={{ marginTop: 10 }}
/>
<CustomButton
title="Disabled Button"
onPress={() => console.log('This won\'t fire')}
disabled={true}
style={{ marginTop: 10 }}
/>
</View>
);
};
```_
## Navigation
### React Navigation Setup
```bash
# Install React Navigation
npm install @react-navigation/native
# Install dependencies
npm install react-native-screens react-native-safe-area-context
# For iOS, install pods
cd ios && pod install && cd ..
# Install navigators
npm install @react-navigation/stack
npm install @react-navigation/bottom-tabs
npm install @react-navigation/drawer
npm install @react-navigation/material-top-tabs
# Install gesture handler (for stack navigator)
npm install react-native-gesture-handler
# For Android, add to MainActivity.java
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
```_
### Web-Screen-Navigation
```javascript
// navigation/AppNavigator.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from '../screens/HomeScreen';
import DetailsScreen from '../screens/DetailsScreen';
import ProfileScreen from '../screens/ProfileScreen';
const Stack = createStackNavigator();
const AppNavigator = () => {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: {
backgroundColor: '#007AFF',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'My Home' }}
/>
<Stack.Screen
name="Details"
component={DetailsScreen}
options={({ route }) => ({ title: route.params.title })}
/>
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={{
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default AppNavigator;
```_
### Tab Navigator
```javascript
// navigation/TabNavigator.js
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons';
import HomeScreen from '../screens/HomeScreen';
import SearchScreen from '../screens/SearchScreen';
import ProfileScreen from '../screens/ProfileScreen';
const Tab = createBottomTabNavigator();
const TabNavigator = () => {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'Search') {
iconName = focused ? 'search' : 'search-outline';
} else if (route.name === 'Profile') {
iconName = focused ? 'person' : 'person-outline';
}
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: '#007AFF',
tabBarInactiveTintColor: 'gray',
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Search" component={SearchScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
};
export default TabNavigator;
```_
### Navigation Haken
```javascript
// screens/HomeScreen.js
import React from 'react';
import { View, Text, Button } from 'react-native';
import { useNavigation, useRoute, useFocusEffect } from '@react-navigation/native';
const HomeScreen = () => {
const navigation = useNavigation();
const route = useRoute();
// Execute code when screen comes into focus
useFocusEffect(
React.useCallback(() => {
console.log('Screen is focused');
return () => {
console.log('Screen is unfocused');
};
}, [])
);
const navigateToDetails = () => {
navigation.navigate('Details', {
itemId: 86,
title: 'Details Screen',
otherParam: 'anything you want here',
});
};
const goBack = () => {
navigation.goBack();
};
const resetNavigation = () => {
navigation.reset({
index: 0,
routes: [{ name: 'Home' }],
});
};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button title="Go to Details" onPress={navigateToDetails} />
<Button title="Go Back" onPress={goBack} />
<Button title="Reset Navigation" onPress={resetNavigation} />
</View>
);
};
export default HomeScreen;
// screens/DetailsScreen.js
import React from 'react';
import { View, Text, Button } from 'react-native';
const DetailsScreen = ({ route, navigation }) => {
const { itemId, title, otherParam } = route.params;
React.useEffect(() => {
// Update header title
navigation.setOptions({ title: title });
}, [navigation, title]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Text>Item ID: {itemId}</Text>
<Text>Other Param: {otherParam}</Text>
<Button
title="Update Title"
onPress={() => navigation.setOptions({ title: 'Updated!' })}
/>
<Button
title="Go to Details... again"
onPress={() =>
navigation.push('Details', {
itemId: Math.floor(Math.random() * 100),
title: 'New Details',
})
}
/>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
);
};
export default DetailsScreen;
```_
### Drawer Navigator
```javascript
// navigation/DrawerNavigator.js
import React from 'react';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { View, Text, StyleSheet } from 'react-native';
import HomeScreen from '../screens/HomeScreen';
import ProfileScreen from '../screens/ProfileScreen';
import SettingsScreen from '../screens/SettingsScreen';
const Drawer = createDrawerNavigator();
// Custom drawer content
const CustomDrawerContent = (props) => {
return (
<View style={styles.drawerContainer}>
<View style={styles.drawerHeader}>
<Text style={styles.drawerHeaderText}>My App</Text>
</View>
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
</DrawerContentScrollView>
</View>
);
};
const DrawerNavigator = () => {
return (
<Drawer.Navigator
drawerContent={(props) => <CustomDrawerContent {...props} />}
screenOptions={{
drawerStyle: {
backgroundColor: '#f6f6f6',
width: 240,
},
drawerActiveTintColor: '#007AFF',
drawerInactiveTintColor: '#666',
}}
>
<Drawer.Screen
name="Home"
component={HomeScreen}
options={{
drawerIcon: ({ color }) => (
<Ionicons name="home-outline" size={22} color={color} />
),
}}
/>
<Drawer.Screen
name="Profile"
component={ProfileScreen}
options={{
drawerIcon: ({ color }) => (
<Ionicons name="person-outline" size={22} color={color} />
),
}}
/>
<Drawer.Screen
name="Settings"
component={SettingsScreen}
options={{
drawerIcon: ({ color }) => (
<Ionicons name="settings-outline" size={22} color={color} />
),
}}
/>
</Drawer.Navigator>
);
};
const styles = StyleSheet.create({
drawerContainer: {
flex: 1,
},
drawerHeader: {
height: 100,
backgroundColor: '#007AFF',
justifyContent: 'center',
alignItems: 'center',
},
drawerHeaderText: {
color: 'white',
fontSize: 18,
fontWeight: 'bold',
},
});
export default DrawerNavigator;
```_
## Staatliche Verwaltung
### React Hooks
```javascript
// hooks/useCounter.js
import { useState, useCallback } from 'react';
const useCounter = (initialValue = 0) => {
const [count, setCount] = useState(initialValue);
const increment = useCallback(() => {
setCount(prev => prev + 1);
}, []);
const decrement = useCallback(() => {
setCount(prev => prev - 1);
}, []);
const reset = useCallback(() => {
setCount(initialValue);
}, [initialValue]);
return { count, increment, decrement, reset };
};
export default useCounter;
// Usage in component
import React from 'react';
import { View, Text, Button } from 'react-native';
import useCounter from '../hooks/useCounter';
const CounterScreen = () => {
const { count, increment, decrement, reset } = useCounter(0);
return (
<View style={styles.container}>
<Text style={styles.countText}>Count: {count}</Text>
<Button title="Increment" onPress={increment} />
<Button title="Decrement" onPress={decrement} />
<Button title="Reset" onPress={reset} />
</View>
);
};
```_
### Context API
```javascript
// context/AuthContext.js
import React, { createContext, useContext, useReducer, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
const AuthContext = createContext();
const authReducer = (state, action) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...state,
userToken: action.token,
isLoading: false,
};
case 'SIGN_IN':
return {
...state,
isSignout: false,
userToken: action.token,
};
case 'SIGN_OUT':
return {
...state,
isSignout: true,
userToken: null,
};
default:
return state;
}
};
export const AuthProvider = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, {
isLoading: true,
isSignout: false,
userToken: null,
});
useEffect(() => {
const bootstrapAsync = async () => {
let userToken;
try {
userToken = await AsyncStorage.getItem('userToken');
} catch (e) {
// Restoring token failed
}
dispatch({ type: 'RESTORE_TOKEN', token: userToken });
};
bootstrapAsync();
}, []);
const authContext = {
signIn: async (data) => {
// Simulate API call
const userToken = 'dummy-auth-token';
await AsyncStorage.setItem('userToken', userToken);
dispatch({ type: 'SIGN_IN', token: userToken });
},
signOut: async () => {
await AsyncStorage.removeItem('userToken');
dispatch({ type: 'SIGN_OUT' });
},
signUp: async (data) => {
// Simulate API call
const userToken = 'dummy-auth-token';
await AsyncStorage.setItem('userToken', userToken);
dispatch({ type: 'SIGN_IN', token: userToken });
},
};
return (
<AuthContext.Provider value={{ ...state, ...authContext }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
// Usage in App.js
import React from 'react';
import { AuthProvider } from './context/AuthContext';
import AppNavigator from './navigation/AppNavigator';
const App = () => {
return (
<AuthProvider>
<AppNavigator />
</AuthProvider>
);
};
export default App;
```_
### Redux Setup
```bash
# Install Redux
npm install redux react-redux @reduxjs/toolkit
```_
```javascript
// store/store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
import authReducer from './authSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
auth: authReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// store/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
value: 0,
};
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
// Usage in component
import React from 'react';
import { View, Text, Button } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount } from '../store/counterSlice';
const CounterScreen = () => {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<View style={styles.container}>
<Text style={styles.countText}>Count: {count}</Text>
<Button title="+" onPress={() => dispatch(increment())} />
<Button title="-" onPress={() => dispatch(decrement())} />
<Button title="+5" onPress={() => dispatch(incrementByAmount(5))} />
</View>
);
};
// App.js with Redux Provider
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './store/store';
import AppNavigator from './navigation/AppNavigator';
const App = () => {
return (
<Provider store={store}>
<AppNavigator />
</Provider>
);
};
export default App;
```_
## Styling
### StyleSheet
```javascript
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const StylingExample = () => {
return (
<View style={styles.container}>
<Text style={styles.title}>Styling in React Native</Text>
<View style={styles.box}>
<Text style={styles.boxText}>Styled Box</Text>
</View>
<View style={[styles.box, styles.redBox]}>
<Text style={[styles.boxText, styles.whiteText]}>Red Box</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
color: '#333',
},
box: {
width: 200,
height: 100,
backgroundColor: '#007AFF',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 20,
borderRadius: 10,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5, // Android shadow
},
boxText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
redBox: {
backgroundColor: '#FF3B30',
},
whiteText: {
color: 'white',
},
});
export default StylingExample;
```_
### Flexbox-Layout
```javascript
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const FlexboxExample = () => {
return (
<View style={styles.container}>
{/* Flex Direction */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Flex Direction: Row</Text>
<View style={[styles.flexContainer, styles.row]}>
<View style={[styles.box, styles.box1]} />
<View style={[styles.box, styles.box2]} />
<View style={[styles.box, styles.box3]} />
</View>
</View>
{/* Justify Content */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Justify Content: Space Between</Text>
<View style={[styles.flexContainer, styles.spaceBetween]}>
<View style={[styles.box, styles.box1]} />
<View style={[styles.box, styles.box2]} />
<View style={[styles.box, styles.box3]} />
</View>
</View>
{/* Align Items */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Align Items: Center</Text>
<View style={[styles.flexContainer, styles.alignCenter]}>
<View style={[styles.box, styles.box1, { height: 30 }]} />
<View style={[styles.box, styles.box2, { height: 50 }]} />
<View style={[styles.box, styles.box3, { height: 40 }]} />
</View>
</View>
{/* Flex */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Flex: 1, 2, 1</Text>
<View style={[styles.flexContainer, styles.row]}>
<View style={[styles.box, styles.box1, { flex: 1 }]} />
<View style={[styles.box, styles.box2, { flex: 2 }]} />
<View style={[styles.box, styles.box3, { flex: 1 }]} />
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
section: {
marginBottom: 30,
},
sectionTitle: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 10,
color: '#333',
},
flexContainer: {
height: 80,
backgroundColor: '#e0e0e0',
borderRadius: 5,
},
row: {
flexDirection: 'row',
},
spaceBetween: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 10,
},
alignCenter: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: 50,
height: 50,
borderRadius: 5,
},
box1: {
backgroundColor: '#FF6B6B',
},
box2: {
backgroundColor: '#4ECDC4',
},
box3: {
backgroundColor: '#45B7D1',
},
});
export default FlexboxExample;
```_
### Responsive Design
```javascript
import React from 'react';
import { View, Text, StyleSheet, Dimensions } from 'react-native';
const { width, height } = Dimensions.get('window');
const ResponsiveExample = () => {
return (
<View style={styles.container}>
<Text style={styles.title}>Responsive Design</Text>
{/* Percentage-based width */}
<View style={styles.percentageBox}>
<Text style={styles.boxText}>50% Width</Text>
</View>
{/* Screen dimension-based */}
<View style={styles.dimensionBox}>
<Text style={styles.boxText}>Screen Width: {width}</Text>
</View>
{/* Aspect ratio */}
<View style={styles.aspectRatioBox}>
<Text style={styles.boxText}>Aspect Ratio 16:9</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
},
percentageBox: {
width: '50%',
height: 80,
backgroundColor: '#007AFF',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 20,
borderRadius: 10,
},
dimensionBox: {
width: width * 0.8,
height: 80,
backgroundColor: '#FF3B30',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 20,
borderRadius: 10,
},
aspectRatioBox: {
width: '100%',
aspectRatio: 16 / 9,
backgroundColor: '#34C759',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 10,
},
boxText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
textAlign: 'center',
},
});
export default ResponsiveExample;
```_
### Stilierte Komponenten
```bash
# Install styled-components
npm install styled-components
npm install --save-dev @types/styled-components-react-native
```_
```javascript
import React from 'react';
import styled from 'styled-components/native';
const Container = styled.View`
flex: 1;
padding: 20px;
background-color: #f5f5f5;
`;
const Title = styled.Text`
font-size: 24px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
color: #333;
`;
const StyledButton = styled.TouchableOpacity`
background-color: ${props => props.primary ? '#007AFF' : '#FF3B30'};
padding: 15px 30px;
border-radius: 10px;
margin-bottom: 10px;
align-items: center;
`;
const ButtonText = styled.Text`
color: white;
font-size: 16px;
font-weight: 600;
`;
const StyledComponentsExample = () => {
return (
<Container>
<Title>Styled Components</Title>
<StyledButton primary onPress={() => console.log('Primary pressed')}>
<ButtonText>Primary Button</ButtonText>
</StyledButton>
<StyledButton onPress={() => console.log('Secondary pressed')}>
<ButtonText>Secondary Button</ButtonText>
</StyledButton>
</Container>
);
};
export default StyledComponentsExample;
```_
## APIs und Services
### Fetch API
```javascript
// services/apiService.js
const API_BASE_URL = 'https://jsonplaceholder.typicode.com';
class ApiService {
static async get(endpoint) {
try {
const response = await fetch(`${API_BASE_URL}${endpoint}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('GET request failed:', error);
throw error;
}
}
static async post(endpoint, data) {
try {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('POST request failed:', error);
throw error;
}
}
static async put(endpoint, data) {
try {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('PUT request failed:', error);
throw error;
}
}
static async delete(endpoint) {
try {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.status === 204 ? null : await response.json();
} catch (error) {
console.error('DELETE request failed:', error);
throw error;
}
}
}
export default ApiService;
// Usage in component
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import ApiService from '../services/apiService';
const PostsScreen = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchPosts();
}, []);
const fetchPosts = async () => {
try {
setLoading(true);
const data = await ApiService.get('/posts');
setPosts(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const renderPost = ({ item }) => (
<View style={styles.postItem}>
<Text style={styles.postTitle}>{item.title}</Text>
<Text style={styles.postBody}>{item.body}</Text>
</View>
);
if (loading) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" color="#007AFF" />
</View>
);
}
if (error) {
return (
<View style={styles.centered}>
<Text style={styles.errorText}>Error: {error}</Text>
</View>
);
}
return (
<FlatList
data={posts}
renderItem={renderPost}
keyExtractor={item => item.id.toString()}
style={styles.container}
/>
);
};
```_
### Axios Integration
```bash
# Install Axios
npm install axios
```_
```javascript
// services/httpClient.js
import axios from 'axios';
import AsyncStorage from '@react-native-async-storage/async-storage';
const httpClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor
httpClient.interceptors.request.use(
async (config) => {
const token = await AsyncStorage.getItem('authToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Response interceptor
httpClient.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
if (error.response?.status === 401) {
// Handle unauthorized access
await AsyncStorage.removeItem('authToken');
// Navigate to login screen
}
return Promise.reject(error);
}
);
export default httpClient;
// services/userService.js
import httpClient from './httpClient';
export const userService = {
getUsers: () => httpClient.get('/users'),
getUser: (id) => httpClient.get(`/users/${id}`),
createUser: (userData) => httpClient.post('/users', userData),
updateUser: (id, userData) => httpClient.put(`/users/${id}`, userData),
deleteUser: (id) => httpClient.delete(`/users/${id}`),
};
```_
### AsyncStorage
```bash
# Install AsyncStorage
npm install @react-native-async-storage/async-storage
# For iOS
cd ios && pod install && cd ..
```_
```javascript
// utils/storage.js
import AsyncStorage from '@react-native-async-storage/async-storage';
export const storage = {
// Store data
setItem: async (key, value) => {
try {
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem(key, jsonValue);
} catch (error) {
console.error('Error storing data:', error);
}
},
// Retrieve data
getItem: async (key) => {
try {
const jsonValue = await AsyncStorage.getItem(key);
return jsonValue != null ? JSON.parse(jsonValue) : null;
} catch (error) {
console.error('Error retrieving data:', error);
return null;
}
},
// Remove data
removeItem: async (key) => {
try {
await AsyncStorage.removeItem(key);
} catch (error) {
console.error('Error removing data:', error);
}
},
// Clear all data
clear: async () => {
try {
await AsyncStorage.clear();
} catch (error) {
console.error('Error clearing storage:', error);
}
},
// Get all keys
getAllKeys: async () => {
try {
return await AsyncStorage.getAllKeys();
} catch (error) {
console.error('Error getting keys:', error);
return [];
}
},
};
// Usage in component
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button } from 'react-native';
import { storage } from '../utils/storage';
const StorageExample = () => {
const [name, setName] = useState('');
const [savedName, setSavedName] = useState('');
useEffect(() => {
loadSavedName();
}, []);
const loadSavedName = async () => {
const saved = await storage.getItem('userName');
if (saved) {
setSavedName(saved);
}
};
const saveName = async () => {
await storage.setItem('userName', name);
setSavedName(name);
setName('');
};
const clearName = async () => {
await storage.removeItem('userName');
setSavedName('');
};
return (
<View style={styles.container}>
<Text style={styles.title}>AsyncStorage Example</Text>
<TextInput
style={styles.input}
placeholder="Enter your name"
value={name}
onChangeText={setName}
/>
<Button title="Save Name" onPress={saveName} />
<Button title="Clear Name" onPress={clearName} />
{savedName ? (
<Text style={styles.savedText}>Saved Name: {savedName}</Text>
) : (
<Text style={styles.noDataText}>No saved name</Text>
)}
</View>
);
};
```_
## Native Module
### Zugriff auf Geräteeigenschaften
```javascript
// Camera and Image Picker
import { launchImageLibrary, launchCamera } from 'react-native-image-picker';
const ImagePickerExample = () => {
const [imageUri, setImageUri] = useState(null);
const selectImage = () => {
const options = {
mediaType: 'photo',
includeBase64: false,
maxHeight: 2000,
maxWidth: 2000,
};
launchImageLibrary(options, (response) => {
| if (response.didCancel | | response.error) { |
console.log('User cancelled or error');
} else {
setImageUri(response.assets[0].uri);
}
});
};
const takePhoto = () => {
const options = {
mediaType: 'photo',
includeBase64: false,
maxHeight: 2000,
maxWidth: 2000,
};
launchCamera(options, (response) => {
| if (response.didCancel | | response.error) { |
console.log('User cancelled or error');
} else {
setImageUri(response.assets[0].uri);
}
});
};
return (
<View style={styles.container}>
<Button title="Select from Gallery" onPress={selectImage} />
<Button title="Take Photo" onPress={takePhoto} />
{imageUri && (
<Image source={{ uri: imageUri }} style={styles.image} />
)}
</View>
);
};
// Geolocation
import Geolocation from '@react-native-community/geolocation';
const LocationExample = () => {
const [location, setLocation] = useState(null);
const getCurrentLocation = () => {
Geolocation.getCurrentPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
});
},
(error) => {
console.error('Error getting location:', error);
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }
);
};
return (
<View style={styles.container}>
<Button title="Get Current Location" onPress={getCurrentLocation} />
{location && (
<Text>
Latitude: {location.latitude}, Longitude: {location.longitude}
</Text>
)}
</View>
);
};
// Permissions
import { PermissionsAndroid, Platform } from 'react-native';
const requestCameraPermission = async () => {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Camera Permission',
message: 'App needs camera permission to take photos',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
}
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
} catch (err) {
console.warn(err);
return false;
}
}
return true; // iOS permissions handled automatically
};
```_
### Erstellung kundenspezifischer Native Module
```javascript
// Android: android/app/src/main/java/com/yourapp/CustomModule.java
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;
public class CustomModule extends ReactContextBaseJavaModule {
CustomModule(ReactApplicationContext context) {
super(context);
}
@Override
public String getName() {
return "CustomModule";
}
@ReactMethod
public void multiply(int a, int b, Promise promise) {
try {
int result = a * b;
promise.resolve(result);
} catch (Exception e) {
promise.reject("CALCULATION_ERROR", e);
}
}
}
// iOS: ios/CustomModule.h
#import <React/RCTBridgeModule.h>
@interface CustomModule : NSObject <RCTBridgeModule>
@end
// iOS: ios/CustomModule.m
#import "CustomModule.h"
@implementation CustomModule
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(multiply:(int)a withB:(int)b
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
{
NSNumber *result = @(a * b);
resolve(result);
}
@end
// JavaScript usage
import { NativeModules } from 'react-native';
const { CustomModule } = NativeModules;
const useCustomModule = () => {
const multiply = async (a, b) => {
try {
const result = await CustomModule.multiply(a, b);
return result;
} catch (error) {
console.error('Error in custom module:', error);
throw error;
}
};
return { multiply };
};
```_
## Leistung
### Optimierungstechnik
```javascript
// React.memo for component optimization
import React, { memo } from 'react';
const ExpensiveComponent = memo(({ data, onPress }) => {
console.log('ExpensiveComponent rendered');
return (
<View style={styles.container}>
<Text>{data.title}</Text>
<Button title="Press me" onPress={onPress} />
</View>
);
});
// useCallback for function memoization
import React, { useState, useCallback } from 'react';
const ParentComponent = () => {
const [count, setCount] = useState(0);
const [data] = useState({ title: 'Static data' });
const handlePress = useCallback(() => {
console.log('Button pressed');
}, []);
return (
<View>
<Text>Count: {count}</Text>
<Button title="Increment" onPress={() => setCount(count + 1)} />
<ExpensiveComponent data={data} onPress={handlePress} />
</View>
);
};
// useMemo for expensive calculations
import React, { useMemo } from 'react';
const ExpensiveCalculation = ({ items }) => {
const expensiveValue = useMemo(() => {
console.log('Calculating expensive value...');
return items.reduce((sum, item) => sum + item.value, 0);
}, [items]);
return <Text>Total: {expensiveValue}</Text>;
};
```_
### FlatList Optimierung
```javascript
import React, { useState, useCallback } from 'react';
import { FlatList, View, Text, StyleSheet } from 'react-native';
const OptimizedFlatList = () => {
const [data] = useState(
Array.from({ length: 1000 }, (_, index) => ({
id: index.toString(),
title: `Item ${index}`,
subtitle: `Subtitle ${index}`,
}))
);
const renderItem = useCallback(({ item }) => (
<View style={styles.item}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subtitle}>{item.subtitle}</Text>
</View>
), []);
const keyExtractor = useCallback((item) => item.id, []);
const getItemLayout = useCallback(
(data, index) => ({
length: 80, // Item height
offset: 80 * index,
index,
}),
[]
);
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={50}
/>
);
};
const styles = StyleSheet.create({
item: {
height: 80,
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
title: {
fontSize: 16,
fontWeight: 'bold',
},
subtitle: {
fontSize: 14,
color: '#666',
},
});
```_
### Bildoptimierung
```javascript
import React from 'react';
import { Image, View } from 'react-native';
import FastImage from 'react-native-fast-image';
const ImageOptimization = () => {
return (
<View>
{/* Standard Image with optimization */}
<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={styles.image}
resizeMode="cover"
defaultSource={require('./assets/placeholder.png')}
loadingIndicatorSource={require('./assets/loading.png')}
/>
{/* FastImage for better performance */}
<FastImage
style={styles.image}
source={{
uri: 'https://example.com/image.jpg',
priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable,
}}
resizeMode={FastImage.resizeMode.cover}
onLoad={() => console.log('Image loaded')}
onError={() => console.log('Image load error')}
/>
</View>
);
};
```_
### Bundle Größe Optimierung
```javascript
// metro.config.js
const { getDefaultConfig } = require('metro-config');
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts },
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter(ext => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
},
// Enable bundle splitting
serializer: {
createModuleIdFactory: function () {
return function (path) {
// Use shorter module IDs
return path.substr(1).replace(/\//g, '_');
};
},
},
};
})();
// Dynamic imports for code splitting
const LazyComponent = React.lazy(() => import('./LazyComponent'));
const App = () => {
return (
<Suspense fallback={<ActivityIndicator />}>
<LazyComponent />
</Suspense>
);
};
```_
## Prüfung
### Jest Setup
```bash
# Jest is included with React Native by default
# Install additional testing utilities
npm install --save-dev @testing-library/react-native
npm install --save-dev @testing-library/jest-native
```_
```javascript
// __tests__/App.test.js
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import '@testing-library/jest-native/extend-expect';
import App from '../App';
describe('App', () => {
it('renders correctly', () => {
const { getByText } = render(<App />);
expect(getByText('Welcome to React Native!')).toBeTruthy();
});
it('handles button press', async () => {
const { getByText, getByTestId } = render(<App />);
const button = getByTestId('test-button');
fireEvent.press(button);
await waitFor(() => {
expect(getByText('Button pressed!')).toBeTruthy();
});
});
});
// Component testing
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import CustomButton from '../components/CustomButton';
describe('CustomButton', () => {
it('calls onPress when pressed', () => {
const mockOnPress = jest.fn();
const { getByText } = render(
<CustomButton title="Test Button" onPress={mockOnPress} />
);
fireEvent.press(getByText('Test Button'));
expect(mockOnPress).toHaveBeenCalledTimes(1);
});
it('is disabled when disabled prop is true', () => {
const { getByText } = render(
<CustomButton title="Test Button" disabled={true} />
);
const button = getByText('Test Button').parent;
expect(button).toBeDisabled();
});
});
```_
### Detox E2E Prüfung
```bash
# Install Detox
npm install --save-dev detox
# Initialize Detox
npx detox init
# Install Detox CLI
npm install -g detox-cli
```_
```javascript
// e2e/firstTest.e2e.js
describe('Example', () => {
beforeAll(async () => {
await device.launchApp();
});
beforeEach(async () => {
await device.reloadReactNative();
});
it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});
it('should show hello screen after tap', async () => {
await element(by.id('hello_button')).tap();
await expect(element(by.text('Hello!!!'))).toBeVisible();
});
it('should show world screen after tap', async () => {
await element(by.id('world_button')).tap();
await expect(element(by.text('World!!!'))).toBeVisible();
});
});
// .detoxrc.json
{
"testRunner": "jest",
"runnerConfig": "e2e/config.json",
"configurations": {
"ios.sim.debug": {
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/YourApp.app",
"build": "xcodebuild -workspace ios/YourApp.xcworkspace -scheme YourApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"device": {
"type": "iPhone 12"
}
},
"android.emu.debug": {
"binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
"build": "cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Pixel_4_API_30"
}
}
}
}
```_
### Mocking
```javascript
// __mocks__/@react-native-async-storage/async-storage.js
export default {
setItem: jest.fn(() => Promise.resolve()),
getItem: jest.fn(() => Promise.resolve(null)),
removeItem: jest.fn(() => Promise.resolve()),
clear: jest.fn(() => Promise.resolve()),
getAllKeys: jest.fn(() => Promise.resolve([])),
};
// __tests__/services/apiService.test.js
import ApiService from '../services/apiService';
// Mock fetch
global.fetch = jest.fn();
describe('ApiService', () => {
beforeEach(() => {
fetch.mockClear();
});
it('should fetch data successfully', async () => {
const mockData = { id: 1, title: 'Test Post' };
fetch.mockResolvedValueOnce({
ok: true,
json: async () => mockData,
});
const result = await ApiService.get('/posts/1');
expect(fetch).toHaveBeenCalledWith(
'https://jsonplaceholder.typicode.com/posts/1'
);
expect(result).toEqual(mockData);
});
it('should handle fetch errors', async () => {
fetch.mockRejectedValueOnce(new Error('Network error'));
await expect(ApiService.get('/posts/1')).rejects.toThrow('Network error');
});
});
```_
## Debugging
### React Native Debugger
```bash
# Install React Native Debugger
# Download from: https://github.com/jhen0409/react-native-debugger
# Enable debugging in app
# Shake device or press Cmd+D (iOS) / Cmd+M (Android)
# Select "Debug JS Remotely"
```_
### Flipper Integration
```bash
# Flipper is included by default in React Native 0.62+
# Download Flipper desktop app from: https://fbflipper.com/
# Install Flipper plugins
npm install --save-dev react-native-flipper
```_
```javascript
// utils/flipper.js
import { logger } from 'react-native-logs';
const defaultConfig = {
severity: __DEV__ ? 'debug' : 'error',
transport: __DEV__ ? logger.consoleTransport : logger.fileAsyncTransport,
transportOptions: {
colors: {
info: 'blueBright',
warn: 'yellowBright',
error: 'redBright',
},
},
};
const log = logger.createLogger(defaultConfig);
export default log;
// Usage in components
import log from '../utils/flipper';
const MyComponent = () => {
const handlePress = () => {
log.debug('Button pressed');
log.info('User interaction logged');
};
return (
<Button title="Press me" onPress={handlePress} />
);
};
```_
### Leistungsüberwachung
```javascript
// utils/performance.js
import { InteractionManager } from 'react-native';
export const measurePerformance = (name, fn) => {
return (...args) => {
const start = Date.now();
const result = fn(...args);
if (result instanceof Promise) {
return result.finally(() => {
const end = Date.now();
console.log(`${name} took ${end - start}ms`);
});
} else {
const end = Date.now();
console.log(`${name} took ${end - start}ms`);
return result;
}
};
};
export const runAfterInteractions = (fn) => {
return InteractionManager.runAfterInteractions(fn);
};
// Usage
const expensiveOperation = measurePerformance('expensiveOperation', () => {
// Expensive computation
return heavyCalculation();
});
// Run after animations complete
runAfterInteractions(() => {
// Heavy operation that shouldn't block UI
processLargeDataSet();
});
```_
### Fehlersuche
```javascript
// components/ErrorBoundary.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
// Log to crash reporting service
// crashlytics().recordError(error);
}
render() {
if (this.state.hasError) {
return (
<View style={styles.container}>
<Text style={styles.title}>Something went wrong</Text>
<Text style={styles.error}>{this.state.error?.message}</Text>
<Button
title="Try again"
onPress={() => this.setState({ hasError: false, error: null })}
/>
</View>
);
}
return this.props.children;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 10,
},
error: {
fontSize: 14,
color: 'red',
textAlign: 'center',
marginBottom: 20,
},
});
export default ErrorBoundary;
// Usage in App.js
import ErrorBoundary from './components/ErrorBoundary';
const App = () => {
return (
<ErrorBoundary>
<AppNavigator />
</ErrorBoundary>
);
};
```_
## Bereitstellung
### Android Bereitstellung
```bash
# Generate signed APK
cd android
./gradlew assembleRelease
# Generate AAB (Android App Bundle)
./gradlew bundleRelease
# Install release APK
adb install app/build/outputs/apk/release/app-release.apk
```_
```javascript
// android/app/build.gradle
android {
...
signingConfigs {
release {
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
storeFile file(MYAPP_UPLOAD_STORE_FILE)
storePassword MYAPP_UPLOAD_STORE_PASSWORD
keyAlias MYAPP_UPLOAD_KEY_ALIAS
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
}
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
}
// android/gradle.properties
MYAPP_UPLOAD_STORE_FILE=my-upload-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=*****
MYAPP_UPLOAD_KEY_PASSWORD=*****
```_
### iOS Bereitstellung
```bash
# Archive for distribution
xcodebuild -workspace ios/MyApp.xcworkspace \
-scheme MyApp \
-configuration Release \
-archivePath ios/build/MyApp.xcarchive \
archive
# Export IPA
xcodebuild -exportArchive \
-archivePath ios/build/MyApp.xcarchive \
-exportPath ios/build \
-exportOptionsPlist ios/ExportOptions.plist
```_
### Schnelle Automatisierung
```bash
# Install Fastlane
sudo gem install fastlane
# Initialize Fastlane
cd ios && fastlane init
cd android && fastlane init
```_
```ruby
# ios/fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "Build and upload to TestFlight"
lane :beta do
increment_build_number(xcodeproj: "MyApp.xcodeproj")
build_app(workspace: "MyApp.xcworkspace", scheme: "MyApp")
upload_to_testflight
end
desc "Build and upload to App Store"
lane :release do
increment_build_number(xcodeproj: "MyApp.xcodeproj")
build_app(workspace: "MyApp.xcworkspace", scheme: "MyApp")
upload_to_app_store
end
end
# android/fastlane/Fastfile
default_platform(:android)
platform :android do
desc "Build and upload to Play Console"
lane :beta do
gradle(task: "clean bundleRelease")
upload_to_play_store(track: 'beta')
end
desc "Deploy to Play Store"
lane :release do
gradle(task: "clean bundleRelease")
upload_to_play_store
end
end
```_
### CodePush (Over-the-Air Updates)
```bash
# Install CodePush CLI
npm install -g code-push-cli
# Install CodePush SDK
npm install --save react-native-code-push
# Register app
code-push app add MyApp-iOS ios react-native
code-push app add MyApp-Android android react-native
```_
```javascript
// App.js with CodePush
import codePush from 'react-native-code-push';
const App = () => {
return (
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
);
};
const codePushOptions = {
checkFrequency: codePush.CheckFrequency.ON_APP_RESUME,
installMode: codePush.InstallMode.ON_NEXT_RESUME,
};
export default codePush(codePushOptions)(App);
// Release update
// code-push release-react MyApp-iOS ios
// code-push release-react MyApp-Android android
```_
## Plattform-Specific Code
### Plattform Modul
```javascript
import { Platform } from 'react-native';
const PlatformExample = () => {
return (
<View style={styles.container}>
<Text>Platform: {Platform.OS}</Text>
<Text>Version: {Platform.Version}</Text>
{Platform.OS === 'ios' && (
<Text>This is iOS specific content</Text>
)}
{Platform.OS === 'android' && (
<Text>This is Android specific content</Text>
)}
<View style={Platform.select({
ios: styles.iosContainer,
android: styles.androidContainer,
})}>
<Text>Platform specific styling</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
iosContainer: {
backgroundColor: '#f0f0f0',
borderRadius: 10,
},
androidContainer: {
backgroundColor: '#e0e0e0',
elevation: 5,
},
});
```_
### Plattformspezifische Dateien
```javascript
// components/Button.ios.js
import React from 'react';
import { TouchableOpacity, Text } from 'react-native';
const Button = ({ title, onPress }) => (
<TouchableOpacity style={styles.iosButton} onPress={onPress}>
<Text style={styles.iosButtonText}>{title}</Text>
</TouchableOpacity>
);
// components/Button.android.js
import React from 'react';
import { TouchableNativeFeedback, Text, View } from 'react-native';
const Button = ({ title, onPress }) => (
<TouchableNativeFeedback onPress={onPress}>
<View style={styles.androidButton}>
<Text style={styles.androidButtonText}>{title}</Text>
</View>
</TouchableNativeFeedback>
);
// Usage (React Native automatically picks the right file)
import Button from './components/Button';
```_
### Sicherheit und Sicherheit
```javascript
import React from 'react';
import { SafeAreaView, StatusBar, Platform } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const SafeAreaExample = () => {
const insets = useSafeAreaInsets();
return (
<SafeAreaView style={styles.container}>
<StatusBar
barStyle={Platform.OS === 'ios' ? 'dark-content' : 'light-content'}
backgroundColor={Platform.OS === 'android' ? '#007AFF' : undefined}
/>
<View style={{
paddingTop: insets.top,
paddingBottom: insets.bottom,
paddingLeft: insets.left,
paddingRight: insets.right,
}}>
<Text>Content with safe area padding</Text>
</View>
</SafeAreaView>
);
};
Bibliotheken Dritter Teil
Beliebte Bibliotheken
# UI Libraries
npm install react-native-elements
npm install react-native-paper
npm install native-base
# Navigation
npm install @react-navigation/native
npm install @react-navigation/stack
npm install @react-navigation/bottom-tabs
# State Management
npm install redux react-redux @reduxjs/toolkit
npm install zustand
npm install recoil
# Networking
npm install axios
npm install react-query
# Storage
npm install @react-native-async-storage/async-storage
npm install react-native-mmkv
# Media
npm install react-native-image-picker
npm install react-native-video
npm install react-native-sound
# Maps
npm install react-native-maps
# Animations
npm install react-native-reanimated
npm install lottie-react-native
# Forms
npm install react-hook-form
npm install formik
# Utils
npm install react-native-device-info
npm install react-native-permissions
npm install react-native-keychain
```_
### Beispiele für die Integration von Bibliotheken
```javascript
// React Native Elements
import React from 'react';
import { Header, Button, Card } from 'react-native-elements';
import Icon from 'react-native-vector-icons/FontAwesome';
const ElementsExample = () => (
<View>
<Header
centerComponent={{ text: 'MY TITLE', style: { color: '#fff' } }}
rightComponent={{ icon: 'home', color: '#fff' }}
/>
<Card title="CARD WITH DIVIDER">
<Text>Card content goes here</Text>
<Button
icon={<Icon name="code" size={15} color="white" />}
buttonStyle={{ borderRadius: 0, marginLeft: 0, marginRight: 0, marginBottom: 0 }}
title="VIEW NOW"
/>
</Card>
</View>
);
// React Hook Form
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import { TextInput, Button, Text } from 'react-native';
const FormExample = () => {
const { control, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<View>
<Controller
control={control}
rules={{ required: true }}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
placeholder="Email"
onBlur={onBlur}
onChangeText={onChange}
value={value}
style={styles.input}
/>
)}
name="email"
/>
{errors.email && <Text>Email is required.</Text>}
<Button title="Submit" onPress={handleSubmit(onSubmit)} />
</View>
);
};
// React Native Reanimated
import React from 'react';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
const AnimatedExample = () => {
const translateX = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [{ translateX: translateX.value }],
};
});
const handlePress = () => {
translateX.value = withSpring(translateX.value + 50);
};
return (
<View>
<Animated.View style={[styles.box, animatedStyle]} />
<Button title="Move" onPress={handlePress} />
</View>
);
};
```_
## Best Practices
### Code Organisation
src/ ├── components/ # Reusable UI components │ ├── common/ # Generic components │ ├── forms/ # Form-specific components │ └── navigation/ # Navigation components ├── screens/ # Screen components │ ├── auth/ # Authentication screens │ ├── main/ # Main app screens │ └── settings/ # Settings screens ├── services/ # API and external services ├── utils/ # Utility functions ├── hooks/ # Custom React hooks ├── context/ # React context providers ├── navigation/ # Navigation configuration ├── assets/ # Images, fonts, etc. ├── constants/ # App constants └── types/ # TypeScript type definitions ```_
Performance Best Practices
```javascript
// 1. Use FlatList for large lists
const LargeList = ({ data }) => (
// 2. Optimize images
const OptimizedImage = ({ uri }) => (
// 3. Use InteractionManager for heavy operations import { InteractionManager } from 'react-native';
const HeavyOperationComponent = () => { useEffect(() => { InteractionManager.runAfterInteractions(() => { // Heavy operation that shouldn't block UI performHeavyOperation(); }); }, []);
return
// 4. Minimize bridge communication const MinimizeBridge = () => { // Bad: Multiple bridge calls const badExample = () => { Animated.timing(value1, { toValue: 100 }).start(); Animated.timing(value2, { toValue: 200 }).start(); Animated.timing(value3, { toValue: 300 }).start(); };
// Good: Batch operations const goodExample = () => { Animated.parallel([ Animated.timing(value1, { toValue: 100 }), Animated.timing(value2, { toValue: 200 }), Animated.timing(value3, { toValue: 300 }), ]).start(); }; }; ```_
Sicherheit Best Practices
```javascript // 1. Secure storage for sensitive data import { Keychain } from 'react-native-keychain';
const secureStorage = { setItem: async (key, value) => { await Keychain.setInternetCredentials(key, key, value); },
getItem: async (key) => { try { const credentials = await Keychain.getInternetCredentials(key); return credentials ? credentials.password : null; } catch (error) { return null; } },
removeItem: async (key) => { await Keychain.resetInternetCredentials(key); }, };
// 2. Certificate pinning
const secureApiCall = async () => {
const response = await fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Authorization': Bearer ${await secureStorage.getItem('token')}
,
},
});
return response.json(); };
// 3. Input validation const validateInput = (input) => { // Sanitize and validate user input const sanitized = input.trim().replace(/[<>]/g, '');
if (sanitized.length < 3) { throw new Error('Input too short'); }
return sanitized; }; ```_
Fehlerbehebung
Gemeinsame Themen
```bash
Metro bundler issues
npx react-native start --reset-cache
iOS build issues
cd ios && pod install && cd .. npx react-native run-ios --clean
Android build issues
cd android && ./gradlew clean && cd .. npx react-native run-android --clean
Clear all caches
npx react-native start --reset-cache rm -rf node_modules && npm install cd ios && pod install && cd ..
Fix permission issues (macOS)
sudo chown -R $(whoami) ~/.npm sudo chown -R $(whoami) /usr/local/lib/node_modules ```_
Häufige Fehler
```javascript // Network request failed const handleNetworkError = async () => { try { const response = await fetch('https://api.example.com/data'); return await response.json(); } catch (error) { if (error.message === 'Network request failed') { // Check network connectivity // Verify API endpoint // Check CORS settings } throw error; } };
// Element type is invalid // Usually caused by incorrect import/export // Check component imports and exports
// Cannot read property of undefined
const SafeComponent = ({ user }) => {
// Use optional chaining
return (
// Memory leaks const ComponentWithCleanup = () => { useEffect(() => { const subscription = someService.subscribe();
return () => {
// Cleanup subscriptions
subscription.unsubscribe();
};
}, []);
return
--
Zusammenfassung
React Native ist ein leistungsstarker Rahmen für den Aufbau von plattformübergreifenden mobilen Anwendungen, der Folgendes bietet:
- *Cross-Platform Entwicklung: Schreiben Sie einmal, laufen auf iOS und Android
- Native Performance: Direkter Zugriff auf native Plattform-APIs und Komponenten
- *Hot Reloading: Schneller Entwicklungszyklus mit sofortigem Feedback
- *Rich Ecosystem: Umfangreiche Bibliotheksökosysteme und Gemeinschaftsunterstützung
- *Familiar Development: Verwenden Sie React-Konzepte und JavaScript/TypeScript
- *Flexible Architecture: Unterstützt verschiedene staatliche Management- und Navigationslösungen
- ** Herstellungsfertig**: Verwendet von großen Unternehmen wie Facebook, Instagram und Airbnb
React Native zeichnet sich durch die schnelle mobile App-Entwicklung aus, wobei native Performance und plattformspezifische Nutzererlebnisse erhalten bleiben. Sein reifes Ökosystem, umfassendes Tooling und eine starke Community-Unterstützung machen es zu einer exzellenten Wahl für Teams, die hochwertige mobile Anwendungen effizient aufbauen möchten.
<= <= <= <================================================================================= Funktion copyToClipboard() {\cHFFFF} const commands = document.querySelectorAll('code'); alle Befehle = ''; Befehle. Für jede(cmd) => alle Befehle += cmd.textContent + '\n'); navigator.clipboard.writeText (allCommands); Alarm ('Alle Befehle, die in die Zwischenablage kopiert werden!'); }
Funktion generierenPDF() { Fenster.print(); }