React Native: Xây Dựng App iOS & Android Bằng JavaScript
Tìm hiểu React Native - framework của Meta cho phép developer web dễ dàng chuyển sang mobile development
React Native: JavaScript Cho Mobile Development
React Native là framework của Meta (Facebook) cho phép bạn xây dựng ứng dụng mobile thực sự (không phải hybrid) bằng JavaScript và React. Nếu bạn đã biết web development, đây là cách nhanh nhất để bắt đầu làm mobile app!
Tại Sao Chọn React Native?
1. Dùng Kiến Thức Web Có Sẵn
// Nếu bạn biết React web, bạn đã biết 80% React Native!
import React, { useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
// Component y chang React web
function Counter() {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.title}>Đếm: {count}</Text>
<Button
title="Tăng"
onPress={() => setCount(count + 1)}
/>
</View>
);
}
// StyleSheet giống CSS
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
}
});
// Nếu biết React, học RN chỉ mất vài ngày! 🚀
2. Native Performance
Chú thích: React Native không phải là WebView (như Cordova/PhoneGap). Nó biên dịch thành native UI components thật sự!
// <View> → UIView (iOS) hoặc android.view (Android)
// <Text> → UILabel (iOS) hoặc TextView (Android)
// <Image> → UIImageView (iOS) hoặc ImageView (Android)
// App chạy smooth như native app! ⚡
3. Fast Refresh - DX Tuyệt Vời
// Sửa code, thấy kết quả NGAY LẬP TỨC
// Không cần rebuild app!
function WelcomeScreen() {
return (
<View>
<Text>Xin chào!</Text>
{/* Thay đổi text này và save file */}
{/* App update < 1 giây! ⚡ */}
</View>
);
}
Setup Project
Expo - Cách Dễ Nhất
Chú thích: Expo là bộ công cụ giúp bạn bắt đầu React Native mà không cần cài đặt Xcode/Android Studio phức tạp.
# Cài Expo CLI
npm install -g expo-cli
# Tạo project mới
npx create-expo-app MyApp
# Vào thư mục
cd MyApp
# Chạy app
npm start
# Quét QR code bằng điện thoại → App chạy ngay! 📱
React Native CLI - Full Control
# Cài React Native CLI
npm install -g react-native-cli
# Tạo project
npx react-native init MyApp
# Chạy trên iOS
npx react-native run-ios
# Chạy trên Android
npx react-native run-android
# Cần Xcode (Mac) hoặc Android Studio
Core Components
Layout với Flexbox
Chú thích: Flexbox trong React Native mặc định là flexDirection: 'column' (khác với web là row)
import { View, Text } from 'react-native';
function FlexboxLayout() {
return (
<View style={{ flex: 1, flexDirection: 'column' }}>
{/* Header */}
<View style={{ height: 80, backgroundColor: '#007AFF' }}>
<Text style={{ color: 'white', fontSize: 20, padding: 20 }}>
Header
</Text>
</View>
{/* Content - chiếm hết không gian còn lại */}
<View style={{ flex: 1, backgroundColor: '#F5F5F5' }}>
<Text>Nội dung chính</Text>
</View>
{/* Footer */}
<View style={{ height: 60, backgroundColor: '#333' }}>
<Text style={{ color: 'white' }}>Footer</Text>
</View>
</View>
);
}
ScrollView & FlatList
import { ScrollView, FlatList, Text } from 'react-native';
// ScrollView - Cho nội dung tĩnh
function StaticContent() {
return (
<ScrollView>
<Text>Đoạn 1...</Text>
<Text>Đoạn 2...</Text>
{/* Tất cả render cùng lúc */}
</ScrollView>
);
}
// FlatList - Cho danh sách dài (hiệu quả hơn)
function PostsList() {
const posts = [
{ id: '1', title: 'Bài viết 1' },
{ id: '2', title: 'Bài viết 2' },
// ... 1000 items
];
return (
<FlatList
data={posts}
// Chỉ render items đang hiển thị trên màn hình
// → Performance tốt với 10,000 items! 🚀
renderItem={({ item }) => (
<Text style={{ padding: 20 }}>{item.title}</Text>
)}
keyExtractor={item => item.id}
/>
);
}
TextInput & Form
import { TextInput, Button, Alert } from 'react-native';
import { useState } from 'react';
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleLogin = () => {
if (!email || !password) {
Alert.alert('Lỗi', 'Vui lòng nhập đầy đủ thông tin');
return;
}
// Call API login
console.log('Đăng nhập với:', email, password);
};
return (
<View style={{ padding: 20 }}>
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
style={{
borderWidth: 1,
borderColor: '#ccc',
padding: 10,
marginBottom: 10,
borderRadius: 5,
}}
/>
<TextInput
placeholder="Mật khẩu"
value={password}
onChangeText={setPassword}
secureTextEntry // Ẩn mật khẩu
style={{
borderWidth: 1,
borderColor: '#ccc',
padding: 10,
marginBottom: 20,
borderRadius: 5,
}}
/>
<Button title="Đăng nhập" onPress={handleLogin} />
</View>
);
}
Navigation
React Navigation
Chú thích: React Navigation là thư viện routing phổ biến nhất cho React Native, tương tự như React Router cho web.
# Cài đặt
npm install @react-navigation/native
npm install @react-navigation/native-stack
npm install react-native-screens react-native-safe-area-context
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
// Screen components
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 24, marginBottom: 20 }}>Trang chủ</Text>
<Button
title="Xem chi tiết"
onPress={() => navigation.navigate('Details', {
itemId: 42,
title: 'Sản phẩm ABC'
})}
/>
</View>
);
}
function DetailsScreen({ route, navigation }) {
const { itemId, title } = route.params;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Chi tiết: {title}</Text>
<Text>ID: {itemId}</Text>
<Button
title="Quay lại"
onPress={() => navigation.goBack()}
/>
</View>
);
}
// App với Navigation
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Trang chủ' }}
/>
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{ title: 'Chi tiết' }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Tab Navigation
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons';
const Tab = createBottomTabNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Trang chủ') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'Cài đặt') {
iconName = focused ? 'settings' : 'settings-outline';
}
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
>
<Tab.Screen name="Trang chủ" component={HomeScreen} />
<Tab.Screen name="Tìm kiếm" component={SearchScreen} />
<Tab.Screen name="Cài đặt" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
Styling
StyleSheet API
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#333',
marginBottom: 10,
},
button: {
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 8,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
}
});
// Sử dụng
<View style={styles.container}>
<Text style={styles.title}>Tiêu đề</Text>
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>Nhấn vào đây</Text>
</TouchableOpacity>
</View>
Styled Components
Chú thích: Styled Components cho phép viết CSS-in-JS giống như trong React web.
npm install styled-components
import styled from 'styled-components/native';
const Container = styled.View`
flex: 1;
background-color: #fff;
padding: 20px;
`;
const Title = styled.Text`
font-size: 24px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
`;
const Button = styled.TouchableOpacity`
background-color: #007AFF;
padding: 15px;
border-radius: 8px;
align-items: center;
`;
const ButtonText = styled.Text`
color: white;
font-size: 16px;
font-weight: 600;
`;
// Sử dụng
function MyComponent() {
return (
<Container>
<Title>Tiêu đề</Title>
<Button>
<ButtonText>Nhấn vào đây</ButtonText>
</Button>
</Container>
);
}
API Calls & State Management
Fetch Data
import { useEffect, useState } from 'react';
import { ActivityIndicator, FlatList, Text } from 'react-native';
function PostsList() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchPosts();
}, []);
const fetchPosts = async () => {
try {
const response = await fetch('https://api.example.com/posts');
const data = await response.json();
setPosts(data);
} catch (err) {
setError('Không thể tải dữ liệu');
} finally {
setLoading(false);
}
};
if (loading) {
return <ActivityIndicator size="large" color="#007AFF" />;
}
if (error) {
return <Text style={{ color: 'red' }}>{error}</Text>;
}
return (
<FlatList
data={posts}
renderItem={({ item }) => (
<View style={{ padding: 20, borderBottomWidth: 1 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>
{item.title}
</Text>
<Text style={{ color: '#666', marginTop: 5 }}>
{item.excerpt}
</Text>
</View>
)}
keyExtractor={item => item.id.toString()}
refreshing={loading}
onRefresh={fetchPosts} // Pull to refresh!
/>
);
}
Redux Toolkit
Chú thích: Redux quản lý state global của app, giống như trong React web.
npm install @reduxjs/toolkit react-redux
// store/userSlice.js
import { createSlice } from '@reduxjs/toolkit';
const userSlice = createSlice({
name: 'user',
initialState: {
currentUser: null,
isLoggedIn: false,
},
reducers: {
login: (state, action) => {
state.currentUser = action.payload;
state.isLoggedIn = true;
},
logout: (state) => {
state.currentUser = null;
state.isLoggedIn = false;
}
}
});
export const { login, logout } = userSlice.actions;
export default userSlice.reducer;
// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';
export const store = configureStore({
reducer: {
user: userReducer,
}
});
// App.js
import { Provider } from 'react-redux';
import { store } from './store';
export default function App() {
return (
<Provider store={store}>
<NavigationContainer>
{/* App content */}
</NavigationContainer>
</Provider>
);
}
// Sử dụng trong component
import { useSelector, useDispatch } from 'react-redux';
import { login, logout } from './store/userSlice';
function ProfileScreen() {
const user = useSelector(state => state.user.currentUser);
const dispatch = useDispatch();
return (
<View>
{user ? (
<>
<Text>Xin chào, {user.name}!</Text>
<Button
title="Đăng xuất"
onPress={() => dispatch(logout())}
/>
</>
) : (
<Button
title="Đăng nhập"
onPress={() => dispatch(login({ name: 'John' }))}
/>
)}
</View>
);
}
Native Modules
Camera
npm install react-native-vision-camera
import { Camera, useCameraDevices } from 'react-native-vision-camera';
function CameraScreen() {
const devices = useCameraDevices();
const device = devices.back;
if (device == null) return <Text>Loading...</Text>;
return (
<Camera
style={{ flex: 1 }}
device={device}
isActive={true}
photo={true}
/>
);
}
Location
npm install @react-native-community/geolocation
import Geolocation from '@react-native-community/geolocation';
function LocationScreen() {
const [location, setLocation] = useState(null);
useEffect(() => {
Geolocation.getCurrentPosition(
position => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
});
},
error => console.log(error),
{ enableHighAccuracy: true }
);
}, []);
return (
<View>
{location ? (
<Text>
Vị trí: {location.latitude}, {location.longitude}
</Text>
) : (
<Text>Đang lấy vị trí...</Text>
)}
</View>
);
}
Performance Tips
1. Sử dụng PureComponent/React.memo
import React from 'react';
// Chỉ re-render khi props thay đổi
const PostItem = React.memo(({ title, excerpt }) => {
return (
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>{title}</Text>
<Text>{excerpt}</Text>
</View>
);
});
// Tránh re-render không cần thiết! 🚀
2. Optimize Images
import FastImage from 'react-native-fast-image';
// Thay vì Image component
<FastImage
source={{
uri: 'https://example.com/image.jpg',
priority: FastImage.priority.high,
}}
style={{ width: 300, height: 200 }}
resizeMode={FastImage.resizeMode.cover}
/>
// Cache và load nhanh hơn! ⚡
3. Use FlatList thay vì ScrollView
// ❌ Chậm với list dài
<ScrollView>
{data.map(item => <Item key={item.id} {...item} />)}
</ScrollView>
// ✅ Nhanh với 10,000 items
<FlatList
data={data}
renderItem={({ item }) => <Item {...item} />}
keyExtractor={item => item.id}
windowSize={10} // Chỉ render 10 items
removeClippedSubviews={true}
maxToRenderPerBatch={10}
/>
Testing
npm install --save-dev @testing-library/react-native jest
import { render, fireEvent } from '@testing-library/react-native';
import Counter from './Counter';
test('counter increments', () => {
const { getByText } = render(<Counter />);
const button = getByText('Tăng');
fireEvent.press(button);
expect(getByText('Đếm: 1')).toBeTruthy();
});
Build & Deploy
iOS
# Build cho iOS
cd ios
pod install
cd ..
# Open Xcode
open ios/MyApp.xcworkspace
# Archive → Upload to App Store
Android
# Generate release APK
cd android
./gradlew assembleRelease
# APK tại: android/app/build/outputs/apk/release/
# Upload lên Google Play Console
React Native vs Flutter
| Feature | React Native | Flutter |
|---|---|---|
| Ngôn ngữ | JavaScript (quen thuộc) | Dart (mới học) |
| Community | ⭐⭐⭐⭐⭐ Rất lớn | ⭐⭐⭐⭐ Đang tăng |
| Performance | ⭐⭐⭐⭐ Tốt | ⭐⭐⭐⭐⭐ Rất tốt |
| Native feel | ✅ Native components | ⚠️ Custom rendering |
| Learning curve | Dễ (nếu biết React) | Medium |
| Hot reload | ✅ | ✅ |
Kết Luận
React Native là lựa chọn tuyệt vời nếu:
- ✅ Bạn đã biết JavaScript/React
- ✅ Cần ship app nhanh
- ✅ Team nhỏ, muốn code một lần cho 2 platform
- ✅ App không cần performance cực cao (game 3D)
// Bắt đầu hành trình mobile development!
const yourJourney = {
day1: 'Setup và Hello World',
week1: 'Build todo app',
month1: 'App hoàn chỉnh trên App Store',
future: 'Mobile developer pro! 🚀'
};
console.log('Start coding now! 💪');
Bạn đã build app React Native nào chưa? Chia sẻ trải nghiệm! 💬
Tags
#ReactNative #JavaScript #MobileDevelopment #iOS #Android #CrossPlatform #React