npx react-native init MyApp | 새 프로젝트 생성 |
npx react-native init MyApp --template react-native-template-typescript | TypeScript로 생성 |
npx create-expo-app MyApp | Expo로 생성 |
npm start | Metro 번들러 시작 |
npx react-native run-ios | iOS 시뮬레이터에서 실행 |
npx react-native run-android | Android 에뮬레이터에서 실행 |
npx react-native log-ios | iOS 로그 보기 |
npx react-native log-android | Android 로그 보기 |
cd ios && pod install | iOS 의존성 설치 |
npx react-native bundle --entry-file index.js --bundle-output ./ios/main.jsbundle | iOS 번들 생성 |
cd android && ./gradlew assembleRelease | Android APK 빌드 |
cd android && ./gradlew bundleRelease | Android AAB 빌드 |
import { View, Text, StyleSheet } from 'react-native';
function App() {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello World!</Text>
<Text numberOfLines={2} ellipsizeMode="tail">
Long text that will be truncated...
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 24,
fontWeight: 'bold',
},
}); import { Image } from 'react-native';
// Local image
<Image source={require('./assets/logo.png')} />
// Remote image
<Image
source={{ uri: 'https://example.com/image.png' }}
style={{ width: 200, height: 200 }}
resizeMode="cover" // cover, contain, stretch, center
/>
// With loading placeholder
<Image
source={{ uri: 'https://example.com/image.png' }}
defaultSource={require('./placeholder.png')}
/> import { ScrollView } from 'react-native';
<ScrollView
horizontal={false}
showsVerticalScrollIndicator={true}
contentContainerStyle={{ padding: 20 }}
onScroll={(event) => {
const y = event.nativeEvent.contentOffset.y;
}}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
>
{/* Content */}
</ScrollView> import { TextInput } from 'react-native';
<TextInput
value={text}
onChangeText={setText}
placeholder="Enter text"
placeholderTextColor="#999"
keyboardType="email-address" // default, number-pad, decimal-pad, phone-pad
autoCapitalize="none" // none, sentences, words, characters
autoCorrect={false}
secureTextEntry={false} // For passwords
multiline={false}
maxLength={100}
onSubmitEditing={() => {}}
returnKeyType="done" // done, go, next, search, send
style={{ borderWidth: 1, padding: 10 }}
/> import { Button, TouchableOpacity, TouchableHighlight, Pressable } from 'react-native';
// Simple button
<Button title="Press me" onPress={() => {}} color="#007AFF" />
// TouchableOpacity (fades on press)
<TouchableOpacity onPress={() => {}} activeOpacity={0.7}>
<Text>Press me</Text>
</TouchableOpacity>
// Pressable (more control)
<Pressable
onPress={() => {}}
onLongPress={() => {}}
style={({ pressed }) => [{ opacity: pressed ? 0.5 : 1 }]}
>
<Text>Press me</Text>
</Pressable> import { Switch } from 'react-native';
<Switch
value={isEnabled}
onValueChange={setIsEnabled}
trackColor={{ false: '#767577', true: '#81b0ff' }}
thumbColor={isEnabled ? '#f5dd4b' : '#f4f3f4'}
/>
// For Picker, use @react-native-picker/picker
import { Picker } from '@react-native-picker/picker';
<Picker selectedValue={value} onValueChange={setValue}>
<Picker.Item label="Option 1" value="1" />
<Picker.Item label="Option 2" value="2" />
</Picker> import { FlatList } from 'react-native';
const data = [
{ id: '1', title: 'Item 1' },
{ id: '2', title: 'Item 2' },
];
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item, index }) => (
<View>
<Text>{item.title}</Text>
</View>
)}
ItemSeparatorComponent={() => <View style={{ height: 1 }} />}
ListHeaderComponent={() => <Text>Header</Text>}
ListFooterComponent={() => <Text>Footer</Text>}
ListEmptyComponent={() => <Text>No items</Text>}
onEndReached={() => loadMore()}
onEndReachedThreshold={0.5}
refreshing={isRefreshing}
onRefresh={() => refresh()}
horizontal={false}
numColumns={1}
/> import { SectionList } from 'react-native';
const sections = [
{ title: 'A', data: ['Apple', 'Avocado'] },
{ title: 'B', data: ['Banana', 'Blueberry'] },
];
<SectionList
sections={sections}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Text>{item}</Text>}
renderSectionHeader={({ section: { title } }) => (
<Text style={{ fontWeight: 'bold' }}>{title}</Text>
)}
stickySectionHeadersEnabled={true}
/> import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row', // row, column, row-reverse, column-reverse
justifyContent: 'center', // flex-start, flex-end, center, space-between, space-around
alignItems: 'center', // flex-start, flex-end, center, stretch, baseline
padding: 20,
margin: 10,
backgroundColor: '#fff',
},
text: {
fontSize: 16,
fontWeight: 'bold', // normal, bold, 100-900
color: '#333',
textAlign: 'center', // auto, left, right, center, justify
lineHeight: 24,
},
}); const styles = StyleSheet.create({
parent: {
flex: 1,
flexDirection: 'column',
},
child1: {
flex: 1, // Takes 1/3 of space
},
child2: {
flex: 2, // Takes 2/3 of space
},
absolute: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
}); import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
paddingTop: Platform.OS === 'ios' ? 50 : 20,
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
},
android: {
elevation: 5,
},
}),
},
}); import { useState, useEffect } from 'react';
import { AppState } from 'react-native';
function MyComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchData().then(setData).finally(() => setLoading(false));
}, []);
// App state listener
useEffect(() => {
const subscription = AppState.addEventListener('change', (state) => {
if (state === 'active') {
// App came to foreground
}
});
return () => subscription.remove();
}, []);
return loading ? <ActivityIndicator /> : <Text>{data}</Text>;
} import { useState, useEffect } from 'react';
import { Dimensions, Keyboard } from 'react-native';
// Window dimensions hook
function useWindowDimensions() {
const [dimensions, setDimensions] = useState(Dimensions.get('window'));
useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions(window);
});
return () => subscription?.remove();
}, []);
return dimensions;
}
// Keyboard hook
function useKeyboard() {
const [keyboardHeight, setKeyboardHeight] = useState(0);
useEffect(() => {
const showSub = Keyboard.addListener('keyboardDidShow', (e) => {
setKeyboardHeight(e.endCoordinates.height);
});
const hideSub = Keyboard.addListener('keyboardDidHide', () => {
setKeyboardHeight(0);
});
return () => {
showSub.remove();
hideSub.remove();
};
}, []);
return keyboardHeight;
} // npm install @react-native-async-storage/async-storage
import AsyncStorage from '@react-native-async-storage/async-storage';
// Store data
await AsyncStorage.setItem('@key', 'value');
await AsyncStorage.setItem('@user', JSON.stringify({ id: 1 }));
// Read data
const value = await AsyncStorage.getItem('@key');
const user = JSON.parse(await AsyncStorage.getItem('@user'));
// Remove data
await AsyncStorage.removeItem('@key');
await AsyncStorage.clear(); // Remove all import { Linking } from 'react-native';
// Open URL
await Linking.openURL('https://example.com');
await Linking.openURL('tel:+1234567890');
await Linking.openURL('mailto:test@example.com');
await Linking.openURL('sms:+1234567890');
// Check if can open
const canOpen = await Linking.canOpenURL('https://example.com');
// Handle deep links
useEffect(() => {
const subscription = Linking.addEventListener('url', ({ url }) => {
console.log('Deep link:', url);
});
return () => subscription.remove();
}, []); import { Alert, PermissionsAndroid, Platform } from 'react-native';
// Alert
Alert.alert(
'Title',
'Message',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'OK', onPress: () => console.log('OK') },
],
{ cancelable: true }
);
// Request permission (Android)
if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Camera Permission',
message: 'App needs camera access',
buttonPositive: 'OK',
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
// Permission granted
}
}