React Native Academy · leksjon

Ytelsesjustering av FlatList

Bruk getItemLayout for rader med fast høyde, angi initialNumToRender og windowSize, bruk keyExtractor riktig, og unngå anonyme funksjonsreferanser i renderItem.

Leksjon 3 av 413 trinn

Ytelsesjustering av FlatList er en gratis leksjon i React Native Academy på CoddyKit. Dette er leksjon 3 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i React Native Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i React Native Academy inneholder totalt 4 leksjoner.

Hvorfor FlatList-ytelse er viktig

FlatList er arbeidshesten i React Native-brukergrensesnitt – nesten alle apper har minst én liste. En dårlig konfigurert FlatList fører til tapte bilderammer under rulling, tomme celler som vises mens elementer lastes inn, og unødvendig høyt minneforbruk som kan få appen til å krasje på eldre enheter.

FlatList er en virtualisert liste, som betyr at den bare gjengir elementer som for øyeblikket er synlige på skjermen, samt en liten buffer. Utfordringen er å konfigurere denne bufferen og gjengivelsesprosessen slik at rullingen føles umiddelbar og ikke blokkerer JS-tråden.

Propen keyExtractor

Propen keyExtractor forteller FlatList hvordan hvert element skal identifiseres unikt. React bruker nøkler for effektiv avstemming – når listedataene endres, kobler React nye elementer til eksisterende gjengitte komponenter ved hjelp av nøkkelen, og oppdaterer deretter bare elementene som faktisk er endret.

Bruk alltid en stabil, unik identifikator, for eksempel en database-ID, som nøkkel. Bruk aldri array-indeksen – hvis elementer legges til, fjernes eller omorganiseres, fører indeksbaserte nøkler til at React gjengir komponenter på nytt eller kobler dem feil.

// ❌ Index-based keys — breaks on reorder/insert
<FlatList
  data={posts}
  keyExtractor={(item, index) => String(index)}
  renderItem={renderPost}
/>

// ✅ Stable ID-based keys
<FlatList
  data={posts}
  keyExtractor={(item) => item.id}
  renderItem={renderPost}
/>

getItemLayout for rader med fast høyde

Som standard måler FlatList høyden på hvert gjengitte element for å bygge et kart over rulleposisjoner. Denne målingen skjer på JS-tråden og medfører ekstra kostnader. Hvis alle radene har samme faste høyde, kan du hoppe over målingen med getItemLayout.

Oppgi en funksjon som returnerer høyden, forskyvningen og indeksen for hvert element. Forskyvningen er height * index. Da kan FlatList beregne rulleposisjoner umiddelbart, og scrollToIndex fungerer også som den skal.

const ITEM_HEIGHT = 72;

<FlatList
  data={contacts}
  keyExtractor={(item) => item.id}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
  renderItem={({ item }) => <ContactRow contact={item} />}
/>

initialNumToRender og windowSize

initialNumToRender styrer hvor mange elementer som gjengis ved første montering. Sett den til antallet elementer som er synlige på skjermen uten rulling – flere elementer sløser med tiden for den første gjengivelsen. Standardverdien er 10, noe som ofte er for høyt eller for lavt, avhengig av radhøyden.

windowSize styrer størrelsen på gjengivelsesvinduet som et multiplum av det synlige visningsområdet. En windowSize på 5 betyr at FlatList gjengir 2,5 visningshøyder over og under det synlige området. Lavere verdier reduserer minnebruken, mens høyere verdier reduserer korte glimt av tomme celler ved rask rulling.

<FlatList
  data={posts}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}
  initialNumToRender={8}     // Render 8 items on first paint
  windowSize={5}             // Render 2.5x viewport above/below
  maxToRenderPerBatch={5}    // Render up to 5 new items per JS frame
  updateCellsBatchingPeriod={50} // Check for updates every 50ms
/>

Unngå anonyme renderItem-funksjoner

Hvis du definerer renderItem som en innebygd pilfunksjon i JSX, opprettes en ny funksjonsreferanse ved hver gjengivelse av forelderen. Dette omgår optimaliseringen av avstemmingen i FlatList og omgår også eventuell React.memo du har brukt på radkomponenten.

Definer renderItem som en navngitt funksjon utenfor komponentkroppen (for rene gjengivere), eller med useCallback inne i komponenten når den må lukke over tilstand eller tilbakekallinger. Det samme gjelder for ItemSeparatorComponent og ListEmptyComponent.

// ❌ New reference on every render
return (
  <FlatList
    data={posts}
    renderItem={({ item }) => <PostRow post={item} onPress={handlePress} />}
  />
);

// ✅ Stable reference with useCallback
const renderItem = useCallback(
  ({ item }) => <PostRow post={item} onPress={handlePress} />,
  [handlePress]
);

return <FlatList data={posts} renderItem={renderItem} />;

removeClippedSubviews for store lister

Hvis du setter removeClippedSubviews={true}, ber du det opprinnelige laget om å koble fra visninger som ligger langt utenfor visningsområdet, fra hierarkiet av opprinnelige visninger, slik at GPU-minne frigjøres. Dette er særlig effektivt på Android, der det å holde hundrevis av visninger utenfor skjermen tilkoblet kan svekke rulleytelsen.

Aktiver dette alternativet når listen har mer enn 50–100 elementer. Det finnes et mindre forbehold på iOS: Fjernede visninger kan flimre ved svært rask rulling. Hvis du ser dette problemet, bør du heller redusere windowSize.

<FlatList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}
  removeClippedSubviews={true} // Detach off-screen views from native layer
  windowSize={5}
/>

Unngå tilstand i renderItem

Hvert kall til renderItem bør være en ren funksjon av elementdataene og eventuelle stabile tilbakekallinger. Unngå å lese komponenttilstand eller kontekstverdier i renderItem med mindre de er stabile referanser – hver tilstandsendring vil gjengi hele listen på nytt.

Hvis hver rad trenger dynamiske data (for eksempel om den aktuelle brukeren har likt et innlegg), bør du inkludere disse dataene i selve elementobjektet eller sende dem som en prop fra en stabil velger. Da forblir avstemmingen i FlatList effektiv.

// ❌ Reading volatile state inside renderItem
const renderItem = ({ item }) => (
  <PostRow post={item} isLiked={likedPostIds.includes(item.id)} />
);
// If likedPostIds is in state, all rows re-render on every like

// ✅ Merge liked status into the data array before passing to FlatList
const enrichedPosts = useMemo(
  () => posts.map((p) => ({ ...p, isLiked: likedPostIds.has(p.id) })),
  [posts, likedPostIds]
);

<FlatList data={enrichedPosts} renderItem={({ item }) => (
  <PostRow post={item} />
)} />

Ytelse for trekk-for-å-oppdatere

Propene onRefresh og refreshing legger til en kontroll for trekk-for-å-oppdatere. Sett refreshing til true mens datainnhentingen pågår, og tilbake til false når den er fullført. Dette viser spinneren i den opprinnelige RefreshControl-kontrollen.

For best ytelse bør du unngå setState-kall som fører til unødvendige oppdateringer av FlatList under oppdateringen. Oppdater bare dataene når innhentingen er fullført, slik at FlatList utfører én avstemmingsrunde i stedet for flere mellomliggende.

const [refreshing, setRefreshing] = useState(false);

async function handleRefresh() {
  setRefreshing(true);
  try {
    const fresh = await fetchPosts();
    setPosts(fresh); // Single state update
  } finally {
    setRefreshing(false);
  }
}

<FlatList
  data={posts}
  renderItem={renderItem}
  refreshing={refreshing}
  onRefresh={handleRefresh}
/>

Måling av listeytelse med InteractionManager

InteractionManager utsetter kostnadskrevende arbeid til alle ventende animasjoner og interaksjoner er fullført. Dette er nyttig når innholdet på den første skjermen skal lastes: Animer overgangen mellom skjermbildene først, og hent og gjengi deretter tunge listedata når animasjonen er ferdig.

Pakk inn kostnadskrevende innlasting av de første dataene i InteractionManager.runAfterInteractions for å holde skjermoverganger på 60 bilder per sekund. Uten dette kan en tung første forespørsel blokkere JS-tråden under overgangen og føre til en hakkete innskyvningsanimasjon.

import { InteractionManager } from 'react-native';

useEffect(() => {
  // Defer data load until screen transition completes
  const task = InteractionManager.runAfterInteractions(async () => {
    const data = await fetchLargeDataset();
    setPosts(data);
  });

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

FlashList: Et raskere alternativ

FlashList fra Shopify er en direkte erstatning for FlatList som er betydelig raskere for store lister. Den gjenbruker native cellekomponenter (tilsvarende RecyclerView på Android) i stedet for å opprette nye, noe som reduserer minneallokering og belastningen på søppelryddingen.

Erstatt FlatList med FlashList, og oppgi propen estimatedItemSize i stedet for getItemLayout. De fleste andre FlatList-props er kompatible. FlashList anbefales for lister med mer enn 100 elementer.

import { FlashList } from '@shopify/flash-list';

// Install: npx expo install @shopify/flash-list

<FlashList
  data={posts}
  keyExtractor={(item) => item.id}
  estimatedItemSize={72}   // Average row height in pixels
  renderItem={renderItem}
  onEndReached={loadMore}
  onEndReachedThreshold={0.5}
/>

Profiler FlatList med JS-trådovervåkingen

Etter at De har finjustert FlatList, bør De kontrollere forbedringen med Performance Monitor (utviklermenyen). Følg med på JS FPS-telleren mens De ruller. Hvis den holder seg nær 60 bilder per sekund, gjengis listen effektivt. Et vedvarende fall under 50 bilder per sekund tyder på at JS-tråden er overbelastet under rulling.

Den vanligste gjenværende flaskehalsen etter optimaliseringene ovenfor er innlasting av bilder. Store bilder fra eksterne kilder dekodes på hovedtråden. Bruk FastImage (react-native-fast-image) for å legge til hurtigbufring og progressiv innlasting, slik at bildedekoding ikke blokkerer bilder under rulling.

Kort kontroll

Test forståelsen Deres av konseptene innen React Native-mobilutvikling fra denne leksjonen.

Oppsummering av leksjonen

I denne leksjonen har De lært: hvordan getItemLayout eliminerer målinger av høyde under kjøring for rader med fast høyde, hvordan initialNumToRender og windowSize styrer hvor mange elementer som gjengis rundt visningsområdet, og hvorfor stabile renderItem-referanser er avgjørende for effektiv avstemming i FlatList. Deretter skal vi analysere størrelsen på JavaScript-bunten og ta i bruk lat innlasting for å redusere oppstartstiden til appen.

Gratis å komme i gang

Lær deg JavaScript med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
30
Leksjoner
120

Ofte stilte spørsmål

Er leksjonen «Ytelsesjustering av FlatList» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien React Native Academy, inkludert «Ytelsesjustering av FlatList», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i React Native Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Ytelsesjustering av FlatList»?

Bruk getItemLayout for rader med fast høyde, angi initialNumToRender og windowSize, bruk keyExtractor riktig, og unngå anonyme funksjonsreferanser i renderItem. Du øver på React Native Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med React Native Academy?

Ingen tidligere erfaring er nødvendig. React Native Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Ytelsesjustering av FlatList»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne React Native Academy-leksjonen?

Ja. Alle React Native Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Profilering med Flipper og React DevTools
  2. Memoisering med React.memo, useCallback og useMemo
  3. Ytelsesjustering av FlatList
  4. Pakkestørrelse og lazy loading
← Tilbake til React Native Academy