Agent Skills - Phần 4: React Native Best Practices
Tài liệu tổng hợp React Native Best Practices dùng làm nguồn tham chiếu cho Agent Skills (ví dụ trong Cursor), bao gồm các chủ đề: Components, Styling, Navigation, State, Lists, Performance, Images, Forms, Touch, Animation, Async, Accessibility, Testing và Native modules. Mỗi guideline được đánh giá mức độ quan trọng và có ví dụ code cụ thể. Nội dung dựa trên bộ guideline từ ui-ux-pro-max-skill, giúp developers và AI Agents xây dựng ứng dụng React Native hiệu quả và tuân thủ best practices.
Tài liệu này tổng hợp React Native Best Practices dùng làm nguồn tham chiếu cho Agent Skills (ví dụ trong Cursor).
Nội dung dựa trên bộ guideline từ ui-ux-pro-max-skill — file react-native.csv.
Cập nhật: 08/02/2026.
Mức độ: 🔴 Cao · 🟡 Trung bình · 🟢 Thấp
Ví dụ code: + nên · - tránh
1. Components
1.1 🟡 Use functional components
React Native hiện đại chuẩn là function component + hooks.
- Nên: Dùng functional component với hooks.
+ const App = () => { ... }
- Không nên: Viết class component mới.
- class App extends Component { ... }
📖 Intro to React – React Native
1.2 🟡 Keep components small
Áp dụng Single Responsibility Principle – mỗi component làm một việc rõ ràng.
- Nên: Tách UI thành các component nhỏ: header, content, footer...
+ <Header />
+ <Content />
+ <Footer />
- Không nên: Một component 500+ dòng xử lý mọi thứ.
- 500+ line component
1.3 🟡 Use TypeScript
TypeScript giúp tránh bug runtime, tự hoàn thành tốt hơn.
- Nên: Dùng TypeScript cho project mới, khai báo type cho props/state.
+ const Button: FC<Props> = () => { ... }
- Không nên: Viết JS thuần không type.
- const Button = (props) => { ... }
1.4 🟢 Colocate component files
File component nên đi cùng styles, test, v.v. trong cùng thư mục.
- Nên: Dùng folder riêng cho mỗi component.
+ components/Button/index.tsx
+ components/Button/styles.ts
- Không nên: Cấu trúc phẳng, styles rải rác ở nơi khác.
- components/Button.tsx
- styles/button.ts
2. Styling
2.1 🔴 Use StyleSheet.create
StyleSheet.create tối ưu hoá style object và cảnh báo typo.
- Nên: Khai báo style bằng
StyleSheet.create.
+ const styles = StyleSheet.create({
+ container: { padding: 16 },
+ });
- Không nên: Tạo object style inline trong JSX.
- <View style={{ margin: 10 }} />
2.2 🟡 Avoid inline styles in render
Inline style mỗi lần render tạo object mới, gây re-render không cần thiết.
- Nên: Tham chiếu style từ
styles.*.
+ <View style={styles.container} />
- Không nên: Viết style object trực tiếp trong JSX.
- <View style={{ margin: 10, padding: 5 }} />
2.3 🟡 Use flexbox for layout
Layout chính nên dùng flexbox thay vì absolute mọi nơi.
- Nên: Dùng
flexDirection,alignItems,justifyContent.
+ const styles = StyleSheet.create({
+ row: { flexDirection: 'row', alignItems: 'center' },
+ });
- Không nên: Dựa vào
position: 'absolute'để xếp layout.
- position: 'absolute' everywhere
2.4 🟡 Handle platform differences
Style đôi khi cần khác nhau cho iOS/Android.
- Nên: Dùng
Platform.selecthoặc file.ios.tsx/.android.tsx.
+ const styles = StyleSheet.create({
+ container: Platform.select({
+ ios: { paddingTop: 44 },
+ android: { paddingTop: 0 },
+ }),
+ });
- Không nên: Hard-code giá trị chỉ hợp với một platform.
- Hardcoded iOS values
2.5 🟡 Use responsive dimensions
UI phải scale tốt trên màn hình nhỏ/lớn khác nhau.
- Nên: Dùng
DimensionshoặcuseWindowDimensions.
+ const { width } = useWindowDimensions();
- Không nên: Fix width height bằng pixel cố định cho toàn app.
- width: 375
3. Navigation
3.1 🟡 Use React Navigation
React Navigation là thư viện chuẩn cho routing trong React Native.
- Nên: Dùng
@react-navigation/*cho stack/tab/drawer.
+ const Stack = createStackNavigator();
- Không nên: Tự implement state navigation thủ công.
- Custom navigation state
3.2 🟡 Type navigation params
Navigation nên được type-safe để tránh bug params.
- Nên: Khai báo
RootStackParamListvà dùng generics.
+ navigation.navigate<RootStackParamList>('Home', { id });
- Không nên: Gọi
navigatevới string thuần không type.
- navigation.navigate('Home', { id });
3.3 🟡 Support deep linking
App nên hỗ trợ mở màn hình qua URL/deeplink.
- Nên: Cấu hình prop
linkingcho React Navigation.
+ const linking = { prefixes: ['myapp://', 'https://myapp.com'] };
- Không nên: Bỏ qua deep link, không cấu hình gì.
- No linking configuration
3.4 🔴 Handle Android back button
Back button trên Android cần được xử lý hợp lý (exit, pop stack...).
- Nên: Dùng
useFocusEffect+BackHandler.
+ useFocusEffect(
+ useCallback(() => {
+ const sub = BackHandler.addEventListener('hardwareBackPress', onBack);
+ return () => sub.remove();
+ }, [onBack]),
+ );
- Không nên: Bỏ qua back button khiến UX tệ hoặc crash.
- No back handler
4. State
4.1 🟡 Use useState for local UI state
State UI đơn giản nên dùng useState.
- Nên: Dùng
useStatecho counter, toggle, input...
+ const [count, setCount] = useState(0);
- Không nên: Dùng class component với
this.statecho case đơn giản.
- this.state = { count: 0 };
4.2 🟡 Use useReducer for complex state
State có nhiều field liên quan hoặc logic phức tạp nên dùng useReducer.
- Nên: Gom nhiều field liên quan vào reducer.
+ const [state, dispatch] = useReducer(reducer, initialState);
- Không nên: 5+
useStatecho các giá trị liên quan nhau.
- 5+ useState calls
4.3 🟡 Use context sparingly
Context thích hợp cho theme/auth/locale – không cho dữ liệu đổi liên tục.
- Nên: Dùng context cho theme, auth, locale...
+ <ThemeContext.Provider value={theme}>...</ThemeContext.Provider>
- Không nên: Dùng context cho data list item thay đổi liên tục.
- Context for list item data
4.4 🟡 Consider Zustand or Redux for global state
Global state phức tạp nên dùng riêng store, không prop drilling/useState khắp nơi.
- Nên: Dùng Zustand cho đơn giản, Redux cho domain phức tạp.
+ const useStore = create((set) => ({ ... }));
- Không nên: Dùng
useStaterồi prop drilling cho global state.
- Prop drilling global state
5. Lists
5.1 🔴 Use FlatList for long lists
List dài phải dùng FlatList (virtualized) thay vì ScrollView + map.
- Nên: Dùng
FlatListcho list > 50 items.
+ <FlatList data={items} renderItem={renderItem} />
- Không nên: Map trực tiếp trong
ScrollView.
- <ScrollView>{items.map(renderItem)}</ScrollView>
📖 FlatList
5.2 🔴 Provide keyExtractor
Item trong list cần key ổn định, không dùng index.
- Nên: Dùng
keyExtractorvới ID/stable key.
+ keyExtractor={(item) => item.id}
- Không nên: Dùng index làm key.
- keyExtractor={(_, index) => index.toString()}
5.3 🔴 Optimize renderItem
Item list nên được memo để tránh re-render không cần thiết.
- Nên: Bọc item trong
React.memovà dùng trongrenderItem.
+ renderItem={({ item }) => <MemoizedItem item={item} />}
- Không nên: Inline
renderItemtạo View mới mỗi lần.
- renderItem={({ item }) => <View>...</View>}
5.4 🟡 Use getItemLayout for fixed-height rows
List có item height cố định nên khai báo getItemLayout để tối ưu scroll.
- Nên: Tính sẵn
lengthvàoffset.
+ getItemLayout={(_, index) => ({
+ length: 50,
+ offset: 50 * index,
+ index,
+ })}
- Không nên: Bỏ trống
getItemLayoutvới list dài có fixed height.
- No getItemLayout for fixed height
5.5 🟡 Tune windowSize for large lists
windowSize kiểm soát số item render quanh viewport.
- Nên: Giảm
windowSizecho list cực dài để tiết kiệm RAM.
+ <FlatList windowSize={5} ... />
- Không nên: Dùng
windowSizelớn mặc định cho list rất dài.
- windowSize={21} for huge lists
6. Performance
6.1 🟡 Use React.memo for pure components
Component chỉ phụ thuộc props nên memo để tránh re-render.
- Nên: Export component với
memo.
+ export default memo(MyComponent);
- Không nên: Export component “trần”.
- export default MyComponent;
6.2 🟡 Use useCallback for handlers
Handler truyền xuống child nên được useCallback để ổn định reference.
- Nên: Memo hoá callback theo deps.
+ const handlePress = useCallback(() => { ... }, [deps]);
- Không nên: Tạo function mới inline mỗi lần render.
- onPress={() => doSomething()}
6.3 🟡 Use useMemo for expensive calculations
Tính toán nặng nên được cache bằng useMemo.
- Nên: Dùng
useMemocho logic nặng phụ thuộc deps.
+ const result = useMemo(() => expensive(), [deps]);
- Không nên: Gọi hàm nặng trực tiếp mỗi render.
- const result = expensive();
6.4 🟡 Avoid anonymous functions in JSX
Anonymous function trong JSX tạo reference mới, khiến child re-render.
- Nên: Truyền handler đã khai báo.
+ onPress={handlePress}
- Không nên: Viết inline arrow function trong JSX.
- onPress={() => doSomething()}
6.5 🟡 Use Hermes engine
Hermes giúp cải thiện startup time, memory và overall perf.
- Nên: Bật Hermes trong cấu hình build.
+ hermes_enabled: true
- Không nên: Dùng JavaScriptCore cho project mới mà không lý do.
- hermes_enabled: false
📖 Hermes
7. Images
7.1 🟡 Prefer expo-image for modern image handling
expo-image cung cấp caching, blur, performance tốt hơn cho RN (đặc biệt Expo).
- Nên: Dùng
expo-imagecho list ảnh nặng.
+ <Image source={url} cachePolicy="memory-disk" />
- Không nên: Dùng thư viện không maintain hoặc default Image cho case nặng.
- <FastImage source={url} />
7.2 🔴 Specify image dimensions
Ảnh remote cần width/height rõ để tránh flicker/layout shift.
- Nên: Set width/height trong style.
+ <Image style={{ width: 100, height: 100 }} source={{ uri }} />
- Không nên: Bỏ trống kích thước.
- <Image source={{ uri }} />
7.3 🟢 Use resizeMode
resizeMode điều khiển cách ảnh được scale trong box.
- Nên: Dùng
cover,containhoặc phù hợp use case.
+ <Image resizeMode="cover" ... />
- Không nên: Bỏ
resizeModekhiến mặc định không mong muốn.
- <Image ... />
8. Forms
8.1 🟡 Use controlled inputs
Form nên controlled (value + onChangeText) để state nhất quán.
- Nên: Bind
valuevào state.
+ <TextInput value={text} onChangeText={setText} />
- Không nên: Chỉ dùng
defaultValue.
- <TextInput defaultValue={text} />
8.2 🔴 Handle keyboard properly
Keyboard có thể che mất input/nút submit nếu không xử lý.
- Nên: Dùng
KeyboardAvoidingViewcho form.
+ <KeyboardAvoidingView behavior="padding">
+ <Form />
+ </KeyboardAvoidingView>
- Không nên: Bỏ qua, để keyboard che mất content.
- No keyboard handling
8.3 🟢 Use proper keyboard types
keyboardType nên khớp với loại input (email, number, phone...).
- Nên: Dùng keyboard phù hợp.
+ <TextInput keyboardType="email-address" />
- Không nên: Mọi input đều
default.
- <TextInput keyboardType="default" />
9. Touch & Gestures
9.1 🟢 Use Pressable for new touch interactions
Pressable là API hiện đại, linh hoạt hơn TouchableOpacity.
- Nên: Dùng
Pressablecho touch interactions mới.
+ <Pressable onPress={handlePress}>...</Pressable>
- Không nên: Tiếp tục thêm
TouchableOpacitycho code mới.
- <TouchableOpacity onPress={handlePress}>...</TouchableOpacity>
9.2 🟡 Provide touch feedback
Người dùng cần thấy feedback khi nhấn vào phần tử.
- Nên: Dùng ripple (Android) hoặc opacity change.
+ <Pressable android_ripple={{ color: 'gray' }}>...</Pressable>
- Không nên: Không có bất kỳ feedback nào khi bấm.
- No press feedback
9.3 🟡 Set hitSlop for small targets
Icon nhỏ cần hitSlop để dễ bấm.
- Nên: Tăng vùng chạm bằng
hitSlop.
+ <Pressable hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}>...</Pressable>
- Không nên: Để touch target nhỏ < 44x44.
- 44x44 with no hitSlop
10. Animation & Gestures
10.1 🟡 Use Reanimated for complex animations
react-native-reanimated cho phép animation mượt trên UI thread.
- Nên: Dùng Reanimated cho gesture/animation phức tạp.
+ const offset = useSharedValue(0);
- Không nên: Dùng Animated API cũ cho flow gesture nặng.
- Animated.timing for gesture
10.2 🔴 Run animations on UI thread
Animation chạy trên UI thread sẽ mượt và không phụ thuộc JS thread.
- Nên: Dùng worklet
runOnUI.
+ runOnUI(() => { 'worklet'; /* animation */ })();
- Không nên: Đ ể animation nặng chạy trên JS thread.
- Animated on JS thread
10.3 🟡 Use gesture handler
react-native-gesture-handler cho gesture native performant hơn JS onTouch*.
- Nên: Dùng
GestureDetector/ gesture handler cho gesture phức tạp.
+ <GestureDetector gesture={gesture}>...</GestureDetector>
- Không nên: Dùng
onTouchMoveJS cho mọi gesture.
- <View onTouchMove={...} />
11. Async Logic
11.1 🟡 Handle loading states
Async request nên có loading indicator rõ ràng.
- Nên: Hiển thị
ActivityIndicatortrong khi chờ.
+ {isLoading ? <ActivityIndicator /> : <Content />}
- Không nên: Để màn hình trống trong lúc load.
- No loading state
11.2 🔴 Handle errors gracefully
Lỗi network/API cần UI hiển thị thông báo & retry, không crash app.
- Nên: Có view lỗi rõ ràng.
+ {error ? <ErrorView /> : <Content />}
- Không nên: Không handle error, để app crash hoặc treo.
- No error handling
11.3 🔴 Cancel async operations on unmount
Async request/subscription cần cleanup để tránh memory leak.
- Nên: Dùng
AbortControllerhoặc return cleanup tronguseEffect.
+ useEffect(() => {
+ const controller = new AbortController();
+ fetch(url, { signal: controller.signal });
+ return () => controller.abort();
+ }, [url]);
- Không nên: Không cleanup subscription/timer.
- No cleanup for subscriptions
12. Accessibility
12.1 🔴 Add accessibility labels
Element interactive phải có accessibilityLabel mô tả hành động.
- Nên: Thêm label rõ ràng cho button/icon.
+ <Pressable accessibilityLabel="Submit form">...</Pressable>
- Không nên: Pressable không label.
- <Pressable>...</Pressable>
12.2 🟡 Use accessibility roles
accessibilityRole giúp screen reader hiểu loại control.
- Nên: Gán role phù hợp (button, header, link...).
+ <Pressable accessibilityRole="button">...</Pressable>
- Không nên: Không set role cho button/link.
- <Pressable>...</Pressable>
12.3 🔴 Test with screen readers
App cần được test với TalkBack (Android) và VoiceOver (iOS).
- Nên: Thường xuyên test luồng chính bằng screen reader.
+ Regular TalkBack testing
- Không nên: Bỏ qua hoàn toàn accessibility testing.
- No screen reader testing
13. Testing
13.1 🟡 Use React Native Testing Library
Testing component nên dựa trên behavior (render, event) thay vì implementation.
- Nên: Dùng
@testing-library/react-native.
+ render(<Component />);
- Không nên: Dùng Enzyme hoặc shallow rendering cũ.
- shallow(<Component />);
📖 React Native Testing Library
13.2 🔴 Test on real devices
Simulator không phản ánh đầy đủ behavior (hiệu năng, gesture, camera...).
- Nên: Test trên device thật cho cả iOS và Android.
+ Device testing in CI
- Không nên: Chỉ test trên simulator/emulator.
- Simulator only testing
13.3 🟡 Use Detox for E2E
Flow quan trọng nên có E2E test tự động.
- Nên: Dùng Detox cho E2E của luồng chính.
+ detox test
- Không nên: Chỉ test E2E bằng tay.
- Manual testing only
📖 Detox
14. Native & Permissions
14.1 🔴 Use native modules carefully
Bridge giữa JS và native có overhead – nên batch call.
- Nên: Gom các native call, hạn chế gọi liên tục.
+ Batch updates
- Không nên: Gọi native trên mọi keystroke/scroll nhỏ.
- Call native on every keystroke
14.2 🟢 Use Expo when possible
Expo đơn giản hoá nhiều phần (build, permissions, camera, push...).
- Nên: Dùng Expo nếu app không cần native custom quá phức tạp.
+ expo install package
- Không nên: Dùng bare RN + manual linking cho app đơn giản.
- react-native link package
📖 Expo
14.3 🔴 Handle permissions correctly
Permissions (camera, location, v.v.) phải được request/check đúng cách.
- Nên: Dùng
PermissionsAndroid(Android) hoặc API tương ứng, luôn check trước khi dùng.
+ const granted = await PermissionsAndroid.request(...);
- Không nên: Truy cập tài nguyên trực tiếp mà không kiểm tra permission.
- Access without permission check
Tóm tắt
| Nhóm | Số guideline | Mức độ ưu tiên |
|---|---|---|
| Components | 4 | 🟢–🟡 |
| Styling | 5 | 🟡–🔴 |
| Navigation | 4 | 🟡–🔴 |
| State | 4 | 🟡 |
| Lists | 5 | 🟡–🔴 |
| Performance | 5 | 🟡 |
| Images | 3 | 🟢–🔴 |
| Forms | 3 | 🟡–🔴 |
| Touch | 3 | 🟢–🟡 |
| Animation | 3 | 🟡–🔴 |
| Async | 3 | 🟡–🔴 |
| Accessibility | 3 | 🟡–🔴 |
| Testing | 3 | 🟡–🔴 |
| Native | 3 | 🟢–🔴 |
Nguồn dữ liệu: react-native.csv — ui-ux-pro-max-skill.