Module 03 — React Native Cơ bản

Phải có ⏱ 8-10 giờ 📋 Prerequisites: Module 01, 02

🎯 Mục tiêu học tập

📖 Hướng dẫn học

1 Setup project
Dùng Expo: npx create-expo-app@latest rn-playground hoặc CLI: npx react-native init RNPlayground
Chạy trên emulator/device. Thời gian: ~1h
2 Đọc RN Official Docsreactnative.dev
Focus: Core Components, Style, Layout with Flexbox, Handling Text Input, Lists.
Thời gian: ~3h
3 Xem video tutorial
- "React Native Tutorial" — Net Ninja (YouTube, miễn phí)
- "React Native — The Practical Guide" — Maximilian (Udemy)
Thời gian: ~2h (chọn 1)
4 Clone Instagram Profile Screen
Thực hành layout: header, avatar, stats row, grid photos. Dùng FlatList numColumns.
Thời gian: ~2-3h

⚡ Ôn nhanh

🧭 Vòng lặp học module này

📚 Lý thuyết chi tiết

1. React Native vs React Web

Khía cạnhReact WebReact Native
Render targetDOM (div, span, p)Native Views (UIView, android.view.View)
StylingCSS files, cascade, inheritanceJS objects (StyleSheet), no cascade
LayoutCSS (box model, grid, flexbox)Yoga engine (flexbox only, default column)
Unitspx, em, rem, %, vh/vwdp (density-independent pixels), %
AnimationCSS transitions, Web Animations APIAnimated API, Reanimated
NavigationReact Router (URL-based)React Navigation (stack-based)

Key insight: RN không dùng WebView. JSX compile thành native platform components thông qua Bridge/JSI.

2. Core Components

Layout Components

ComponentVai tròKhi nào dùng
ViewContainer (như div)Mọi nơi cần wrapper, layout
SafeAreaViewTránh notch/status barRoot container trên iOS
KeyboardAvoidingViewĐẩy content lên khi keyboard hiệnScreens có form input
ScrollViewScrollable containerContent ngắn (<50 items), forms

Display Components

ComponentVai tròLưu ý
TextHiển thị textBẮT BUỘC wrap text trong Text (khác web)
ImageHiển thị ảnhrequire() cho local, {uri} cho remote
StatusBarControl status bar stylebarStyle, backgroundColor (Android)
ModalOverlay screenanimationType, transparent

Interactive Components

ComponentVai tròƯu tiên dùng
PressableUniversal pressable wrapper⭐ Recommended (mới nhất)
TouchableOpacityPress với opacity feedbackVẫn phổ biến, quen thuộc
TextInputInput fieldControlled component

List Components

ScrollViewFlatListSectionList
RenderTất cả cùng lúcVirtualized (chỉ render visible)Virtualized + sections
PerformanceKém với nhiều itemsTốt (recycle views)Tốt
Khi nào<50 items, formsLong lists, homogeneousGrouped data (contacts)

3. Styling & StyleSheet

StyleSheet.create() — tạo style objects. Tại sao không dùng inline objects?

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#0f1117',
    paddingHorizontal: 16,
  },
  title: {
    fontSize: 24,
    fontWeight: '700',
    color: '#e4e6f0',
    marginBottom: 8,
  },
  row: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 12,
  },
});

Không có CSS cascade: Mỗi component phải khai báo style riêng. Text bên trong View không kế thừa color từ View (trừ Text nested trong Text).

4. Flexbox Deep Dive

RN default: flexDirection: 'column' (khác web mặc định row)

PropertyGiá trị phổ biếnMẹo nhớ
flexDirectioncolumn | rowcolumn = dọc (default), row = ngang
justifyContentcenter | space-between | flex-startMain axis (theo flexDirection)
alignItemscenter | flex-start | stretchCross axis (vuông góc)
flex: 1numberChiếm hết không gian còn lại
flexWrapwrap | nowrapCó xuống dòng không
gapnumberKhoảng cách giữa children (RN 0.71+)
positionrelative | absoluteabsolute: thoát khỏi flex flow

Mẹo: Khi layout bị sai → check flexDirection trước. Nhớ default là column!

5. Images

// Local (bundled) - require
<Image source={require('./assets/logo.png')} />

// Remote - uri object
<Image
  source={{ uri: 'https://example.com/photo.jpg' }}
  style={{ width: 200, height: 200 }}
/>

Lưu ý quan trọng: Remote images BẮT BUỘC phải set width + height (không tự động như web). Local images RN biết dimensions từ metadata.

resizeModeMô tả
coverScale để fill, crop nếu cần (phổ biến nhất)
containScale vừa khung, không crop, có thể có space
stretchKéo giãn vừa khung, có thể méo
centerKhông scale, center align

6. TextInput

const [email, setEmail] = useState('');

<TextInput
  value={email}
  onChangeText={setEmail}
  placeholder="Email"
  keyboardType="email-address"
  autoCapitalize="none"
  autoCorrect={false}
  returnKeyType="next"
  style={styles.input}
/>

Keyboard types: default, email-address, numeric, phone-pad, decimal-pad

7. Responsive Design

import { useWindowDimensions, PixelRatio } from 'react-native';

const MyComponent = () => {
  const { width, height } = useWindowDimensions();
  const isTablet = width >= 768;

  return (
    <View style={{ padding: isTablet ? 32 : 16 }}>
      {/* responsive content */}
    </View>
  );
};

useWindowDimensions vs Dimensions.get(): useWindowDimensions tự update khi xoay màn hình (reactive). Dimensions.get() là static — cần event listener.

8. Platform-Specific Code

import { Platform } from 'react-native';

const styles = StyleSheet.create({
  shadow: Platform.select({
    ios: {
      shadowColor: '#000',
      shadowOffset: { width: 0, height: 2 },
      shadowOpacity: 0.25,
      shadowRadius: 4,
    },
    android: {
      elevation: 5,
    },
  }),
});

File extensions: Component.ios.tsx / Component.android.tsx — RN tự chọn file đúng platform.

9. KeyboardAvoidingView

Platformbehavior prop
iOS"padding" (recommended)
Android"height" hoặc không cần (android:windowSoftInputMode="adjustResize")

Tip: Dùng react-native-keyboard-aware-scroll-view cho forms phức tạp — tự scroll tới focused input.

10. Ví dụ thực tế: Safe form screen

import { KeyboardAvoidingView, Platform, ScrollView, TextInput } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';

function RegisterScreen() {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      <KeyboardAvoidingView
        style={{ flex: 1 }}
        behavior={Platform.OS === 'ios' ? 'padding' : undefined}
      >
        <ScrollView
          keyboardShouldPersistTaps="handled"
          contentContainerStyle={{ padding: 16, gap: 12 }}
        >
          <TextInput placeholder="Email" keyboardType="email-address" />
          <TextInput placeholder="Password" secureTextEntry />
        </ScrollView>
      </KeyboardAvoidingView>
    </SafeAreaView>
  );
}

❓ Câu hỏi phỏng vấn + Đáp án

Q1: RN render native views hay WebView?

Native views. RN map JSX components sang platform-native views: ViewUIView (iOS) / android.view.View (Android). Không dùng WebView — đây là điểm khác biệt chính so với Cordova/Ionic. Kết quả: performance gần native, nhưng phải bridge giữa JS thread và native thread.

Q2: Tại sao phải wrap text trong <Text>?

RN không có DOM text nodes. Mỗi text phải là Text component → được render thành UILabel (iOS) / TextView (Android). Viết text trực tiếp trong View sẽ crash vì View không biết render text.

Q3: ScrollView vs FlatList — khi nào dùng cái nào?

ScrollView: render TẤT CẢ children cùng lúc. Dùng cho: forms, content ngắn (<50 items), mixed content.

FlatList: virtualized — chỉ render items trong viewport + buffer. Dùng cho: long lists, homogeneous data, dynamic content.

Rule: Nếu list có thể >50 items hoặc dynamic → FlatList. Nếu static content → ScrollView.

Q4: FlatList bị lag — optimize thế nào?
  • getItemLayout — skip layout measurement (nếu items cùng height)
  • keyExtractor — unique stable key (không dùng index)
  • removeClippedSubviews={true} — unmount off-screen items (Android)
  • maxToRenderPerBatch — giảm items render mỗi batch
  • windowSize — giảm viewport buffer
  • React.memo cho renderItem component
  • Dùng @shopify/flash-list thay thế — nhanh hơn đáng kể
Q5: StyleSheet.create() có lợi gì so với inline object?

1) Validation tại build time. 2) Performance: style ID gửi qua Bridge 1 lần. 3) Code organization. Tuy nhiên với New Architecture (JSI), performance gap nhỏ hơn vì không còn serialization overhead.

Q6: flexDirection default trong RN là gì? Tại sao khác web?

Default: column (web default: row). Lý do: mobile screens dọc (portrait) → content tự nhiên flow từ trên xuống. Design decision của Facebook khi tạo Yoga layout engine.

Q7: Xử lý keyboard đè form trên iOS vs Android?

iOS: KeyboardAvoidingView với behavior="padding".

Android: Set android:windowSoftInputMode="adjustResize" trong AndroidManifest.xml — system tự handle. Hoặc dùng behavior="height".

Cả 2: react-native-keyboard-aware-scroll-view — auto scroll tới focused input.

Q8: Platform-specific code — các cách implement?
  1. Platform.OS === 'ios' — simple conditional
  2. Platform.select({ ios: ..., android: ... }) — object mapping
  3. File extensions: .ios.tsx / .android.tsx — khi logic khác nhau nhiều
  4. Platform.Version — check OS version number

🏋️ Bài tập

Bài 1: Clone Instagram Profile Screen
Tạo layout: Header (avatar + stats row: posts/followers/following) + Bio + Grid photos (3 columns).
Yêu cầu: FlatList với numColumns={3}, Image aspect ratio 1:1, responsive cho mọi screen size.
Acceptance: chạy được trên cả iOS và Android, scroll mượt với 50+ photos.
Bài 2: Form Screen với Validation
Tạo Login/Register form: email, password, confirm password, phone.
Yêu cầu: Controlled inputs, validation (email format, password length), keyboard handling, error messages, submit button disabled khi invalid.
Acceptance: keyboard không đè form, validation real-time.
Bài 3: Responsive Grid Layout
Build grid cards hiển thị 2 columns (phone) / 3 columns (tablet).
Yêu cầu: useWindowDimensions, gap giữa cards, responsive padding, platform-specific shadows.

⚠️ Lỗi thường gặp

🏢 Case đi làm thật

Case: Form đăng ký nhìn ổn trên iPhone, nhưng trên Android keyboard che nút Submit. User không biết cách hoàn tất form.

Cách xử lý: Dùng ScrollView với keyboardShouldPersistTaps, cấu hình Android adjustResize, test cả màn hình nhỏ và input cuối form.

🧠 Ghi nhớ nhanh

Nền tảng RN tốt là biết các khác biệt nhỏ giữa web, iOS và Android. Mọi màn hình cơ bản nên được test với keyboard, safe area, màn hình nhỏ, ảnh remote và list đủ dài.

❔ Câu hỏi tự kiểm tra

1. Khi nào ScrollView trở thành vấn đề performance?
2. Vì sao remote Image cần kích thước rõ ràng?
3. SafeAreaView nên đặt ở đâu trong screen?
4. Một layout lệch giữa iOS/Android, bạn kiểm tra những prop nào trước?

✅ Checklist tự đánh giá

📎 Tài nguyên

📘 React Native Official Docs
📺 Net Ninja — React Native Tutorial (YouTube)
📘 Yoga Layout Playground — Interactive flexbox testing
📘 RN Flexbox Docs
📘 react-native-safe-area-context