Nếu đã vững React, có thể skim nhanh. Nếu mới học RN, trang này giúp hiểu React ở mức đủ để đọc Module 02 và Module 04 mà không bị ngợp.
Trong RN, UI không phải HTML. Component trả về các native primitives như View, Text, Pressable, TextInput. Nhưng mental model React vẫn giữ nguyên: props đi xuống, event đi lên, state làm UI render lại.
type UserCardProps = {
name: string;
role: string;
onPress: () => void;
};
function UserCard({ name, role, onPress }: UserCardProps) {
return (
<Pressable onPress={onPress} style={styles.card}>
<Text style={styles.name}>{name}</Text>
<Text style={styles.role}>{role}</Text>
</Pressable>
);
}
Props nên thể hiện component cần gì để render và emit event gì. Component con không nên tự biết quá nhiều về API, navigation, storage nếu không cần.
| Không tốt | Tốt hơn |
|---|---|
UserCard tự fetch user, tự navigate, tự format mọi thứ. |
Parent fetch data; UserCard nhận props và bắn onPress. |
Props kiểu any. |
Props type rõ, optional có default hợp lý. |
Đừng đưa mọi thứ vào state. Chỉ đưa dữ liệu cần render lại UI hoặc cần giữ qua nhiều render.
const [query, setQuery] = useState('');
const [selectedId, setSelectedId] = useState<string | null>(null);
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(query.toLowerCase())
);
filteredUsers là derived data, không cần state riêng nếu tính toán nhẹ.
import { useMemo, useState } from 'react';
import { FlatList, Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
type User = {
id: string;
name: string;
role: 'admin' | 'member';
};
const USERS: User[] = [
{ id: '1', name: 'Tien Dang', role: 'admin' },
{ id: '2', name: 'React Native Dev', role: 'member' },
{ id: '3', name: 'Mobile Engineer', role: 'member' },
];
export function UserListScreen() {
const [query, setQuery] = useState('');
const [selectedId, setSelectedId] = useState<string | null>(null);
const users = useMemo(() => {
const q = query.trim().toLowerCase();
if (!q) return USERS;
return USERS.filter(user => user.name.toLowerCase().includes(q));
}, [query]);
return (
<View style={styles.screen}>
<TextInput
value={query}
onChangeText={setQuery}
placeholder="Search user"
style={styles.input}
/>
<FlatList
data={users}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<Pressable
style={[styles.row, selectedId === item.id && styles.selectedRow]}
onPress={() => setSelectedId(item.id)}
>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.role}>{item.role}</Text>
</Pressable>
)}
ListEmptyComponent={<Text style={styles.empty}>No users found</Text>}
/>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, backgroundColor: '#fff' },
input: {
borderWidth: 1,
borderColor: '#cbd5e1',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 10,
marginBottom: 12,
},
row: {
padding: 14,
borderWidth: 1,
borderColor: '#e2e8f0',
borderRadius: 8,
marginBottom: 8,
},
selectedRow: { borderColor: '#2563eb', backgroundColor: '#eff6ff' },
name: { fontWeight: '700', color: '#0f172a' },
role: { color: '#64748b', marginTop: 4 },
empty: { textAlign: 'center', color: '#64748b', marginTop: 24 },
});
| Lỗi | Vì sao sai? | Fix |
|---|---|---|
| Mutate state trực tiếp | React không thấy reference đổi hoặc UI update khó đoán. | Tạo object/array mới: setItems(prev => [...prev, item]). |
| Dùng index làm key cho list động | Insert/delete/reorder làm React reuse nhầm row. | Dùng id ổn định từ data. |
| Component quá to | Fetch, form, list, modal, navigation dồn một chỗ. | Tách container, presentational component, custom hook khi cần. |
| Derived data thành state riêng | Dễ lệch giữa source state và derived state. | Tính từ state gốc bằng biến thường hoặc useMemo khi nặng. |
Một bug phổ biến là screen có user từ API, copy sang formState, sau đó API trả user mới nhưng form vẫn giữ state cũ. Người mới thường fix bằng cách set state lung tung trong render. Cách đúng là xác định source of truth: form đang edit draft hay sync theo server? Nếu là draft, chỉ reset khi đổi user id hoặc khi user bấm discard.
UserListScreen như ví dụ.UserRow thành component riêng với props rõ ràng.Text; không có div/span như web.