Trang này nối giữa “biết React” và “làm được React Native”. Nó giải thích các khác biệt quan trọng nhất để người mới không áp dụng thói quen web sai vào mobile.
View, Text, ScrollView, FlatList, Image, TextInput, Pressable.React Native dùng React để mô tả UI, nhưng output là native views. View tương đương container native, Text tương đương text native, Image tương đương image native. Vì vậy không có DOM, không có CSS cascade, không có thẻ div.
| Web React | React Native | Ghi nhớ |
|---|---|---|
div | View | Container layout. |
span/p | Text | Text phải nằm trong Text. |
button | Pressable | Tự style state pressed/disabled. |
img | Image | Cần width/height hoặc style rõ. |
input | TextInput | Keyboard, focus, secure input là mobile behavior. |
Trong RN, flexDirection mặc định là column. Kích thước thường phải rõ hơn web, đặc biệt với image, list item, touch target.
const styles = StyleSheet.create({
screen: {
flex: 1,
backgroundColor: '#fff',
},
header: {
height: 56,
justifyContent: 'center',
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
content: {
flex: 1,
padding: 16,
},
});
| Component | Dùng khi | Rủi ro |
|---|---|---|
ScrollView |
Nội dung ít, form, static content. | Render toàn bộ children, list dài sẽ nặng. |
FlatList |
Danh sách động/dài. | Cần key, item height/layout, memoization khi list phức tạp. |
Khi JS thread bị block, app có thể lag dù native UI vẫn tồn tại. Đây là nền để học Module 08 Performance và Module 10 Architecture.
import { Image, Pressable, SafeAreaView, StyleSheet, Text, View } from 'react-native';
export function ProfileSummaryScreen() {
return (
<SafeAreaView style={styles.screen}>
<View style={styles.header}>
<Image
source={{ uri: 'https://placehold.co/96x96/png' }}
style={styles.avatar}
/>
<View style={styles.info}>
<Text style={styles.name}>Mobile Developer</Text>
<Text style={styles.subtitle}>React Native • Kotlin</Text>
</View>
</View>
<View style={styles.statsRow}>
<StatBox label="Apps" value="4" />
<StatBox label="YOE" value="3" />
<StatBox label="Modules" value="18" />
</View>
<Pressable style={styles.primaryButton} onPress={() => {}}>
<Text style={styles.primaryButtonText}>Open portfolio</Text>
</Pressable>
</SafeAreaView>
);
}
function StatBox({ label, value }: { label: string; value: string }) {
return (
<View style={styles.statBox}>
<Text style={styles.statValue}>{value}</Text>
<Text style={styles.statLabel}>{label}</Text>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, backgroundColor: '#f8fafc' },
header: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
padding: 16,
borderRadius: 12,
backgroundColor: '#fff',
},
avatar: { width: 72, height: 72, borderRadius: 36 },
info: { flex: 1 },
name: { fontSize: 20, fontWeight: '700', color: '#0f172a' },
subtitle: { marginTop: 4, color: '#64748b' },
statsRow: { flexDirection: 'row', gap: 10, marginTop: 12 },
statBox: {
flex: 1,
alignItems: 'center',
padding: 14,
borderRadius: 12,
backgroundColor: '#fff',
},
statValue: { fontSize: 20, fontWeight: '700', color: '#2563eb' },
statLabel: { marginTop: 4, color: '#64748b' },
primaryButton: {
minHeight: 48,
alignItems: 'center',
justifyContent: 'center',
marginTop: 16,
borderRadius: 10,
backgroundColor: '#2563eb',
},
primaryButtonText: { color: '#fff', fontWeight: '700' },
});
| Lỗi | Vì sao xảy ra? | Fix |
|---|---|---|
Text nằm trực tiếp trong View |
RN yêu cầu string phải nằm trong Text. |
Bọc bằng <Text>. |
Image không hiện |
Thiếu width/height hoặc URI không hợp lệ. | Đặt style kích thước rõ, kiểm tra network/image source. |
| Button khó bấm | Touch target quá nhỏ. | Dùng min height khoảng 44-48, padding đủ, state disabled rõ. |
| List dài bị lag | Dùng ScrollView render tất cả item. |
Dùng FlatList, key ổn định, tối ưu render item. |
| Layout vỡ giữa Android/iOS | Safe area, font rendering, keyboard, status bar khác nhau. | Test cả hai platform, dùng safe area và keyboard handling đúng. |
Nguyên nhân thường là hard-code width/height, text không cho wrap, touch target quá sát, hoặc không tính safe area/status bar. Cách fix không phải scale toàn bộ theo màn hình, mà là dùng flex, min/max constraints, test device nhỏ, và xác định phần nào scroll được.
ProfileSummaryScreen như ví dụ.ScrollView bằng FlatList cho danh sách 100 item.flexDirection mặc định là column.ScrollView cho nội dung ít; FlatList cho list dài.Text?FlatList thay vì ScrollView?Image cần kích thước rõ?