Module 08 — Performance Optimization

PHẢI CÓ &9201; 8-10 giờ &128203; Prerequisites: Module 01, 02, 03, 04

&127919; MỤC TIÊU HỌC TẬP

&128214; HƯỚNG DẪN HỌC

1 Đọc tài liệu chính thức
Đọc kỹ reactnative.dev/docs/performance — đây là nền tảng để hiểu các bottleneck phổ biến trong RN. Đọc thêm các trang liên quan: Optimizing Flatlist Configuration, RAM Bundles và Inline Requires.
2 Xem video deep-dive
Xem series "React Native Performance" trên kênh Callstack YouTube. Tập trung vào các video về JS thread blocking, bridge overhead, và rendering pipeline.
3 Setup công cụ profiling
Cài đặt Flipper và tích hợp React DevTools Profiler. Làm quen với giao diện: Performance monitor, Network inspector, Layout inspector. Thử chạy profiler trên 1 app demo.
4 Profile 1 app thật
Lấy 1 app bạn đã build (hoặc clone app mẫu), chạy profiler để tìm re-render thừa, JS thread drops, memory leak. Ghi chú lại các bottleneck tìm được và cách fix.
5 Tìm hiểu FlashList
Đọc docs của FlashList by Shopify. So sánh performance với FlatList trên list 10,000+ items. Hiểu cơ chế cell recycling mà FlashList áp dụng.

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

&128218; LÝ THUYẾT CHI TIẾT

1. Threading Model trong React Native

React Native chạy trên 3 thread chính:
Bridge Communication (Old Architecture):

3 thread giao tiếp qua Bridge bằng cách serialize data thành JSON. Đây chính là bottleneck lớn nhất của RN cũ — mỗi lần gửi message qua Bridge đều phải JSON.stringify → truyền → JSON.parse.

New Architecture (Fabric + TurboModules): Sử dụng JSI (JavaScript Interface) cho phép JS gọi trực tiếp C++ functions — không cần serialize/deserialize. Giảm latency đáng kể.

ThreadVai tròNếu bị block
JS ThreadBusiness logic, state, event handlersUI không respond, animations giật
UI ThreadNative rendering, touch, animationsApp freeze, ANR trên Android
Shadow ThreadYoga layout calculationLayout chậm, render delay

Ví dụ flow khi user nhấn button:

User tap (UI Thread)
  → Event serialize to JSON (Bridge)
    → JS Thread nhận event, xử lý handler
      → setState() → tính Virtual DOM diff
        → Gửi layout instructions qua Bridge
          → Shadow Thread tính Yoga layout
            → UI Thread render native views

2. Re-render Optimization

Tại sao component re-render?

React.memo

Wrap component bằng React.memo để skip re-render khi props không đổi (shallow comparison).

const UserCard = React.memo(function UserCard({ name, avatar }: Props) {
  return (
    <View>
      <Image source={{ uri: avatar }} />
      <Text>{name}</Text>
    </View>
  );
});

Component UserCard chỉ re-render khi name hoặc avatar thật sự thay đổi. Nếu parent re-render nhưng truyền cùng props, UserCard sẽ skip.

Cẩn thận: React.memo chỉ shallow compare. Nếu bạn truyền object/array mới mỗi render (dù nội dung giống), memo sẽ vô hiệu. Ví dụ style={{ padding: 10 }} tạo object mới mỗi render → memo không có tác dụng.

useCallback

Memoize function reference để tránh tạo function mới mỗi render. Kết hợp với React.memo mới có ý nghĩa.

const Parent = () => {
  const [count, setCount] = useState(0);

  const handlePress = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  return <Child onPress={handlePress} />;
};

const Child = React.memo(({ onPress }: { onPress: () => void }) => {
  return <Button onPress={onPress} title="Increment" />;
});

Nếu không dùng useCallback, mỗi lần Parent render sẽ tạo handlePress mới → Child luôn re-render dù đã memo.

useMemo

Memoize kết quả tính toán nặng. Chỉ tính lại khi dependencies thay đổi.

const filteredData = useMemo(() => {
  return largeDataset.filter(item =>
    item.name.toLowerCase().includes(searchTerm.toLowerCase())
  );
}, [largeDataset, searchTerm]);

Nếu largeDataset có 50,000 items, filter mỗi render sẽ rất tốn CPU. useMemo cache kết quả cho đến khi searchTerm hoặc largeDataset thay đổi.

Khi nào KHÔNG cần memoize?

Memoize cũng có cost: lưu previous value, so sánh dependencies. Đừng memoize mọi thứ mà không đo đạc.

3. FlatList Optimization

FlatList là windowed list — chỉ render các items trong viewport + buffer. Nhưng nếu không tune đúng, vẫn có thể giật lag khi scroll nhanh trên list dài.
PropDefaultGiải thích
getItemLayout undefined Cung cấp height/offset của mỗi item khi biết trước size. Skip layout measurement → scroll cực mượt, hỗ trợ scrollToIndex instant.
initialNumToRender 10 Số items render ban đầu. Giảm xuống đủ fill 1 screen để giảm initial render time. Quá ít → user thấy blank.
maxToRenderPerBatch 10 Số items render mỗi batch khi scroll. Tăng → ít blank areas nhưng JS thread busy hơn. Giảm → responsive hơn nhưng có thể thấy blank.
windowSize 21 Đơn vị: số viewport heights. windowSize=21 nghĩa là render 10 viewport trên + 1 hiện tại + 10 viewport dưới. Giảm → ít memory nhưng dễ thấy blank khi scroll nhanh.
removeClippedSubviews false Detach views ngoài viewport khỏi native view hierarchy. Giảm memory trên list dài. Cẩn thận: có thể gây bugs với absolute positioning.
keyExtractor item.key / index Phải trả về unique stable key. Dùng index làm key → re-render sai khi data thay đổi thứ tự.

Ví dụ FlatList tối ưu cho list cố định chiều cao:

const ITEM_HEIGHT = 72;

const getItemLayout = (_: any, index: number) => ({
  length: ITEM_HEIGHT,
  offset: ITEM_HEIGHT * index,
  index,
});

<FlatList
  data={data}
  renderItem={renderItem}
  keyExtractor={item => item.id}
  getItemLayout={getItemLayout}
  initialNumToRender={8}
  maxToRenderPerBatch={5}
  windowSize={11}
  removeClippedSubviews={true}
/>

FlashList — Drop-in replacement

FlashList của Shopify sử dụng cell recycling (giống RecyclerView trên Android). Thay vì mount/unmount components khi scroll, FlashList tái sử dụng cell đã render với data mới. Kết quả: render nhanh hơn 5-10x so với FlatList trên list lớn.
import { FlashList } from "@shopify/flash-list";

<FlashList
  data={data}
  renderItem={({ item }) => <UserCard user={item} />}
  estimatedItemSize={72}
  keyExtractor={item => item.id}
/>

estimatedItemSize là prop bắt buộc — FlashList dùng giá trị này để ước lượng scroll position. Không cần getItemLayout vì FlashList tự đo dynamic heights.

4. Hermes Engine

Hermes là JS engine do Meta phát triển, tối ưu riêng cho React Native. Từ RN 0.70+ Hermes là default engine.

AOT (Ahead-of-Time) vs JIT (Just-in-Time)

Đặc điểmJIT (JavaScriptCore)AOT (Hermes)
CompileRuntime — parse + compile khi app chạyBuild time — compile thành bytecode lúc build
Startup timeChậm (phải parse toàn bộ JS bundle)Nhanh (load bytecode trực tiếp)
MemoryCao (lưu AST + compiled code)Thấp (chỉ cần bytecode)
Peak performanceCó thể nhanh hơn (JIT optimize hot paths)Hơi chậm hơn peak (không có JIT optimization)
Bundle sizePlain JS textBytecode nhỏ hơn text
Tại sao Hermes cải thiện startup?
  1. Không cần parse JS source: JSC phải đọc JS text → parse thành AST → compile. Hermes skip bước này vì bytecode đã compile sẵn.
  2. Memory-mapped bytecode: Hermes load bytecode bằng mmap, chỉ load pages cần thiết vào RAM (lazy loading).
  3. Garbage collector tối ưu: Hermes dùng generational GC, allocations nhỏ được collect nhanh hơn.

Kết quả thực tế: giảm 30-50% startup time, giảm 20-30% memory usage.

Kiểm tra Hermes đang bật chưa:

const isHermes = () => !!global.HermesInternal;

console.log("Using Hermes:", isHermes());

5. Image Optimization

Image là một trong những nguyên nhân phổ biến nhất gây lag và crash (OOM). Một ảnh 4000x3000 pixels chiếm ~48MB RAM khi decode (4000 × 3000 × 4 bytes/pixel).

react-native-fast-image

Dựa trên SDWebImage (iOS) và Glide (Android) — 2 thư viện native image loading tốt nhất. So với <Image> mặc định:

import FastImage from "react-native-fast-image";

<FastImage
  style={{ width: 200, height: 200 }}
  source={{
    uri: "https://example.com/photo.jpg",
    priority: FastImage.priority.high,
    cache: FastImage.cacheControl.immutable,
  }}
  resizeMode={FastImage.resizeMode.cover}
/>

Best Practices cho Image

6. InteractionManager.runAfterInteractions

Vấn đề: Khi navigate sang screen mới, animation transition đang chạy. Nếu screen mới ngay lập tức chạy heavy computation (API call, parse data, render list lớn), animation sẽ bị giật.

Giải pháp: InteractionManager.runAfterInteractions defer task cho đến khi tất cả animations/interactions hoàn thành.
import { InteractionManager } from "react-native";

useEffect(() => {
  const task = InteractionManager.runAfterInteractions(() => {
    fetchHeavyData();
    processExpensiveComputation();
  });

  return () => task.cancel();
}, []);

Flow: Navigate → Animation chạy mượt → Animation xong → runAfterInteractions callback được gọi → Heavy task chạy. User thấy transition mượt, sau đó data mới load.

Pattern phổ biến: Hiển thị skeleton/placeholder ngay khi vào screen, defer API call và rendering sau interactions. Kết hợp với ActivityIndicator hoặc skeleton UI.

7. Memory Leaks — 6 Loại Phổ Biến

Memory leak = app chiếm RAM ngày càng nhiều, cuối cùng crash (đặc biệt trên thiết bị low-end).
#Loại LeakNguyên nhânCách Fix
1 Uncleared timers setInterval/setTimeout không clear khi unmount Clear trong useEffect cleanup
2 Event listeners addEventListener không remove khi unmount Remove trong cleanup function
3 Async operations setState sau khi component đã unmount (API callback) AbortController hoặc isMounted flag
4 Closures giữ reference Closure capture biến lớn, không release Nullify references, dùng WeakRef
5 Global state bloat Tích lũy data trong global store không bao giờ cleanup Pagination, eviction policy cho cache
6 Image cache không giới hạn Cache hàng trăm ảnh full-size trong memory Giới hạn cache size, dùng disk cache

Ví dụ fix leak #1 và #3:

useEffect(() => {
  const controller = new AbortController();

  const intervalId = setInterval(() => {
    fetch("/api/status", { signal: controller.signal })
      .then(res => res.json())
      .then(data => setStatus(data))
      .catch(() => {});
  }, 5000);

  return () => {
    clearInterval(intervalId);
    controller.abort();
  };
}, []);

Cleanup function trong useEffect chạy khi component unmount. AbortController cancel pending fetch, clearInterval dừng timer.

8. Bundle Size Optimization

Bundle lớn = startup chậm vì JS engine phải load + parse toàn bộ bundle trước khi render. Trên Hermes, bytecode size ảnh hưởng trực tiếp đến thời gian mmap.

react-native-bundle-visualizer

Công cụ visualize bundle — cho thấy mỗi dependency chiếm bao nhiêu % trong bundle.

npx react-native-bundle-visualizer

Output: treemap interactives hiển thị size của từng module. Từ đó xác định thư viện nào quá nặng cần thay thế hoặc lazy load.

Các kỹ thuật giảm bundle size

import { format } from "date-fns/format";

import { debounce } from "lodash/debounce";

Thay vì import { format } from "date-fns" hoặc import _ from "lodash" sẽ kéo toàn bộ thư viện vào bundle.

9. App Startup Optimization

Cold Start vs Warm Start:

Tối ưu Cold Start

  1. Enable Hermes: Giảm 30-50% parse time nhờ bytecode precompile.
  2. Splash Screen: Dùng react-native-bootsplash hoặc expo-splash-screen để hiển thị splash ngay lập tức (native level), ẩn khi JS bundle ready.
  3. Inline Requires: Lazy load modules — chỉ require khi function đầu tiên gọi đến.
  4. Reduce initial screen complexity: Screen đầu tiên render càng ít component càng tốt.
  5. Defer non-critical initialization: Analytics, crash reporting, push notifications — init sau khi first frame render.
import { BootSplash } from "react-native-bootsplash";

const App = () => {
  useEffect(() => {
    const init = async () => {
      await loadEssentialData();
      await BootSplash.hide({ fade: true });

      InteractionManager.runAfterInteractions(() => {
        initAnalytics();
        initCrashReporting();
        registerPushNotifications();
      });
    };

    init();
  }, []);

  return <Navigation />;
};

Flow: Native splash hiện ngay → JS bundle load → essential data ready → hide splash với fade → defer analytics/crash/push sau khi animation xong.

10. Profiling Tools

ToolMục đíchPlatform
FlipperAll-in-one debugger: network, layout, performance, logsiOS + Android
React DevTools ProfilerMeasure component render time, tìm re-render thừaCross-platform
SystraceLow-level tracing cho JS + native threadsAndroid
Xcode InstrumentsCPU, Memory, GPU profiling chi tiếtiOS
Android Studio ProfilerCPU, Memory, Network, Energy profilingAndroid
Performance MonitorIn-app overlay hiển thị FPS, RAM, JS threadiOS + Android
Why Did You RenderTự động log re-render không cần thiếtCross-platform
Quy trình profiling chuẩn:
  1. Bật Performance Monitor (shake → Show Perf Monitor) để xem FPS realtime
  2. Nếu JS FPS drop < 60: dùng React DevTools Profiler tìm component render lâu
  3. Nếu UI FPS drop: dùng Systrace/Instruments kiểm tra native thread
  4. Kiểm tra memory: Flipper Memory plugin hoặc Instruments Allocations
  5. Fix → đo lại → so sánh trước/sau

&10067; CÂU HỎI PHỎNG VẤN + ĐÁP ÁN

🛠️ Debug playbook: performance bug

Triệu chứngKiểm traFix thường gặp
FlatList scroll giậtRender count, item height, image size, key, JS threadMemo item, stable props, getItemLayout/FlashList, resize image
Input search lagFilter/sort chạy mỗi keypress, list size, deferred workDebounce/deferred value, memo filtered data, move heavy work
Startup chậmTask chạy trước first screen, sync storage, native initDefer non-critical task, lazy init, reduce bundle/startup work
Memory tăng sau khi vào ra screenTimer/listener/request/image cache cleanupCleanup effect, abort request, remove listener, limit cache

Khung trả lời phỏng vấn: Performance phải nói theo vòng đo: triệu chứng, metric, bottleneck, fix nhỏ, đo lại. Tránh trả lời kiểu liệt kê memo/useCallback khi chưa biết bottleneck.

Q1: React Native có mấy thread chính? Mô tả vai trò của từng thread.

3 thread chính:

  • JS Thread: Chạy toàn bộ JavaScript code — business logic, state management, event handlers, React reconciliation. Single-threaded nên nếu block thì UI sẽ không respond.
  • Main/UI Thread: Render native UI components, xử lý touch events, chạy native animations. Đây là thread mà user "nhìn thấy" trực tiếp.
  • Shadow Thread: Chạy Yoga layout engine để tính toán flexbox layout từ style props. Kết quả layout được gửi sang UI Thread để render.

Trong New Architecture (Fabric), Shadow Thread được tích hợp chặt hơn với UI Thread, và JSI thay thế Bridge cho phép synchronous calls giữa JS và native.

Q2: Sự khác biệt giữa React.memo, useCallback, và useMemo? Khi nào dùng cái nào?
  • React.memo: HOC wrap component — skip re-render khi props không đổi (shallow compare). Dùng cho component có render cost cao mà nhận props ổn định.
  • useCallback: Memoize function reference. Dùng khi truyền callback xuống child component đã memo — đảm bảo function reference không đổi giữa các render.
  • useMemo: Memoize giá trị tính toán. Dùng khi có expensive computation (filter/sort/transform large data) mà không cần chạy lại mỗi render.

Nguyên tắc: React.memo cho components, useCallback cho functions, useMemo cho values. Luôn đo đạc trước khi memoize — premature optimization có thể tăng complexity mà không cải thiện performance.

Q3: FlatList có những props nào để tối ưu performance? Giải thích getItemLayout.

Các props quan trọng:

  • getItemLayout: Trả về { length, offset, index } cho mỗi item. Khi biết trước chiều cao cố định, FlatList skip bước đo lường layout → scroll mượt hơn và scrollToIndex hoạt động tức thì.
  • initialNumToRender: Số items render ban đầu, nên đặt vừa đủ fill 1 screen.
  • maxToRenderPerBatch: Số items render mỗi batch khi scroll. Tradeoff giữa fill rate và responsiveness.
  • windowSize: Số viewport heights giữ trong memory. Mặc định 21 (10 trên + 1 hiện tại + 10 dưới).
  • removeClippedSubviews: Detach views ngoài viewport khỏi native hierarchy, giảm memory.

Ngoài ra: luôn dùng keyExtractor với unique stable key, tách renderItem thành memo component riêng, tránh anonymous function trong renderItem.

Q4: Hermes engine khác gì JavaScriptCore? Tại sao cải thiện startup time?

JavaScriptCore (JSC) dùng JIT compilation: parse JS text → compile thành machine code tại runtime. Mỗi lần app start đều phải lặp lại quá trình này.

Hermes dùng AOT compilation: compile JS thành bytecode tại build time. Khi app start, chỉ cần load bytecode — không cần parse/compile.

3 lý do startup nhanh hơn:

  1. Skip parse + compile phase (bước tốn thời gian nhất)
  2. Bytecode được memory-mapped (mmap) → lazy loading, chỉ load pages cần thiết
  3. Bytecode nhỏ hơn JS text → load nhanh hơn từ disk

Tradeoff: Hermes không có JIT optimization nên peak performance có thể hơi thấp hơn JSC cho CPU-intensive tasks. Nhưng cho mobile app, startup time và memory quan trọng hơn peak throughput.

Q5: Kể tên các loại memory leak phổ biến trong React Native và cách fix.
  1. Timers: setInterval/setTimeout không clear → fix bằng clearInterval trong useEffect cleanup.
  2. Event listeners: Không remove khi unmount → return cleanup function từ useEffect.
  3. Async operations: setState sau unmount → dùng AbortController để cancel fetch, hoặc guard với isMounted check.
  4. Closures: Giữ reference đến large objects → nullify references khi không cần, dùng WeakRef nếu phù hợp.
  5. Global store bloat: Cache data không bao giờ cleanup → implement eviction policy, pagination, clear old data.
  6. Image cache: Cache unlimited images → set max cache size, ưu tiên disk cache over memory cache.

Detect: Dùng Flipper Memory plugin hoặc Xcode Instruments Allocations để track memory growth over time.

Q6: InteractionManager.runAfterInteractions dùng để làm gì?

Defer expensive tasks cho đến khi tất cả animations và interactions hiện tại hoàn thành. Use case chính: khi navigate sang screen mới, animation transition đang chạy trên UI thread. Nếu JS thread cũng bận xử lý heavy task (API call, data processing), animation sẽ giật.

runAfterInteractions đặt task vào queue, chờ animation xong mới execute. Kết hợp với skeleton UI: hiển thị placeholder ngay, load data sau khi transition mượt.

Trả về object có method cancel() — luôn cancel trong useEffect cleanup để tránh memory leak.

Q7: Cold start và warm start khác nhau thế nào? Làm sao tối ưu cold start?

Cold start: App process chưa tồn tại trong memory. OS phải: fork process → load native libraries → initialize JS runtime → load JS bundle → execute JS → render first frame. Thường mất 1-5 giây.

Warm start: App đã trong memory (user quay lại từ background). Process vẫn sống, chỉ cần resume activity — gần như instant.

Tối ưu cold start:

  1. Enable Hermes (giảm 30-50% startup)
  2. Native splash screen (hiện trước khi JS ready)
  3. Inline Requires / lazy loading modules
  4. Giảm bundle size (loại bỏ dependencies không cần)
  5. Defer non-critical init (analytics, push) sau first render
  6. Simplify initial screen (ít components, ít computation)
Q8: FlashList khác FlatList như thế nào? Khi nào nên dùng FlashList?

Cơ chế khác biệt cốt lõi: FlatList mount/unmount components khi scroll. FlashList tái sử dụng cells đã mount (cell recycling) — tương tự RecyclerView (Android) hoặc UICollectionView (iOS).

Ưu điểm FlashList:

  • Render nhanh hơn 5-10x trên list lớn (10,000+ items)
  • Ít blank areas khi scroll nhanh
  • Memory usage ổn định (không tăng theo số items đã scroll qua)
  • Drop-in replacement: API tương tự FlatList

Khi nào dùng: List > 100 items, complex render items, user scroll nhanh, thiết bị low-end. Với list < 50 items đơn giản, FlatList đủ tốt.

Q9: Làm sao giảm bundle size trong React Native?
  1. Analyze trước: npx react-native-bundle-visualizer để biết dependency nào chiếm nhiều nhất.
  2. Tree shaking: Import cụ thể: import debounce from "lodash/debounce" thay vì import _ from "lodash".
  3. Thay thư viện nặng: moment.js → dayjs, lodash → native methods hoặc lodash-es.
  4. RAM Bundles: Enable inline requires để lazy load modules.
  5. Remove unused imports: Dùng ESLint rule no-unused-vars.
  6. Code splitting: React.lazy cho screens ít dùng.
  7. Assets: Compress images, dùng vector icons thay PNG, fonts subset.
  8. ProGuard/R8: Minify native code trên Android.
Q10: Bạn sẽ debug performance issue trong React Native như thế nào? Mô tả quy trình.

Quy trình 5 bước:

  1. Reproduce & Identify: Bật Performance Monitor (shake menu). Quan sát JS FPS và UI FPS. Target: cả hai phải ≥ 60fps.
  2. Classify: JS FPS drop → vấn đề ở JS thread (re-render, heavy computation). UI FPS drop → vấn đề ở native (overdraw, complex layout, large images).
  3. Profile JS: React DevTools Profiler để tìm component render lâu, render thừa. "Why Did You Render" library để auto-detect unnecessary re-renders.
  4. Profile Native: Android: Systrace hoặc Android Studio Profiler. iOS: Xcode Instruments (Time Profiler, Allocations).
  5. Fix & Measure: Apply fix cụ thể → đo lại FPS/memory → compare before/after. Lặp lại cho đến khi đạt target.

Common findings: Unnecessary re-renders (70% cases), unoptimized images (15%), heavy computation on JS thread (10%), memory leaks (5%).

&127947; BÀI TẬP

Bài 1: Optimize Re-renders

Cho component tree: App → UserList → UserCard (x100). Mỗi lần search text thay đổi, tất cả 100 UserCard đều re-render dù chỉ filtered list thay đổi.

Yêu cầu:

Acceptance: Khi gõ search, chỉ cards matching mới re-render. Profiler cho thấy giảm ≥ 50% render time.

Bài 2: FlatList vs FlashList Benchmark

Yêu cầu:

Acceptance: Có bảng so sánh số liệu cụ thể giữa 2 approaches. FlashList phải cho FPS cao hơn khi fast scroll.

Bài 3: Memory Leak Hunt

Tạo app có intentional memory leaks (timer không clear, event listener không remove, setState sau unmount). Sau đó dùng Flipper/Instruments để detect và fix từng leak.

Yêu cầu:

Acceptance: Memory graph sau fix phải flat (không tăng liên tục khi navigate back-and-forth).

Bài 4: App Startup Optimization

Yêu cầu:

Acceptance: Cold start time giảm ≥ 20%. Bundle size giảm ≥ 10%.

Bài 5: Image Optimization Pipeline

Yêu cầu:

Acceptance: Version 2 phải load nhanh hơn ≥ 2x, memory thấp hơn ≥ 30%.

&128187; MINI EXERCISE THỰC TẾ REACT NATIVE

Exercise 1: Profiling before/after
Chọn một list lag. Đo render count/FPS trước khi sửa, áp dụng memo/getItemLayout/FlashList, rồi ghi lại số liệu sau khi sửa.
Exercise 2: Image memory drill
Tạo grid ảnh remote. So sánh full-size Image vs thumbnail + cache. Ghi lại load time, memory, scroll smoothness.
Exercise 3: Startup budget
Liệt kê task chạy lúc app start, chuyển task không cần thiết sang sau interaction, và đo cold start lại.

&9888; LỖI THƯỜNG GẶP

&127970; CASE ĐI LÀM THẬT

Case: Feed 200 item giật khi gõ search. Team thêm React.memo nhưng không cải thiện vì renderItem và style object đều tạo mới mỗi render.

Cách xử lý: Đo render count, tách item component, ổn định props/callback/style, memoize filtered data, và kiểm tra lại bằng Profiler thay vì cảm giác.

&129504; GHI NHỚ NHANH

Performance work phải có vòng lặp: đo triệu chứng, tìm bottleneck, sửa nhỏ, đo lại. Không có số liệu thì rất dễ tối ưu sai chỗ.

&10067; CÂU HỎI TỰ KIỂM TRA

1. FlatList lag: bạn đo và kiểm tra gì trước?
2. Khi nào React.memo vô tác dụng?
3. Cold start chậm thường đến từ những nhóm task nào?
4. Memory leak trong RN thường đến từ những nguồn nào?

&10003; CHECKLIST TỰ ĐÁNH GIÁ

&128206; TÀI NGUYÊN

&128214; Tài liệu chính thức
React Native Performance Overview
Optimizing FlatList Configuration
RAM Bundles and Inline Requires
Hermes Engine Documentation
&127909; Video
Callstack YouTube — React Native Performance Series
React Native Performance Optimization (YouTube)
&128230; Thư viện & Công cụ
FlashList by Shopify
react-native-fast-image
Flipper
react-native-bundle-visualizer
Why Did You Render
react-native-bootsplash
&128220; Bài viết chuyên sâu
Callstack Blog — Performance Articles
Shopify Engineering — React Native at Scale
React Native New Architecture