Hubert
9 min
6 marca, 2025

Jak poprawić wydajność aplikacji React Native?

Optymalizacja wydajności aplikacji mobilnych ma ogromne znaczenie dla satysfakcji użytkowników oraz sukcesu produktu. Nawet drobne opóźnienia mogą negatywnie wpłynąć na doświadczenia użytkowników, popularność aplikacji oraz jej ocenę. React Native umożliwia tworzenie aplikacji wieloplatformowych, jednak wymaga odpowiedniej optymalizacji, aby działały szybko i płynnie. W tym artykule znajdziesz praktyczne wskazówki oraz przykłady kodu, które pomogą ci skutecznie poprawić wydajność aplikacji React Native.

Czytaj więcej
Jak poprawić wydajność aplikacji React Native?

React Native korzysta z dwóch osobnych wątków: JavaScript Thread i Native UI Thread. Odpowiednia optymalizacja powinna uwzględniać te różnice, unikając przeciążenia wątku JavaScript oraz delegując jak najwięcej zadań na stronę natywną.

1. Diagnostyka problemów z wydajnością

Zanim rozpoczniesz optymalizację aplikacji mobilnej, musisz wiedzieć, co powoduje problemy. Narzędzia takie jak Flipper, React Native Debugger czy Chrome DevTools pozwolą ci szybko zidentyfikować problematyczne miejsca. Przykładowo Flipper oferuje szczegółowy podgląd czasu renderowania komponentów, użycia pamięci oraz analizy zapytań sieciowych.

Używaj również React DevTools Profiler oraz Hermes profiler – szczególnie jeśli aplikacja korzysta z Hermesa – aby dokładniej przeanalizować zużycie pamięci, czas wykonywania funkcji oraz źródła zbędnych renderowań.

2. Redukcja liczby zbędnych renderów

Unikaj niepotrzebnych renderowań komponentów poprzez odpowiednie praktyki:

  • używaj komponentów funkcyjnych z React.memo oraz opcjonalnego porównywania propsów:
import React from 'react';
import { Text } from 'react-native';

const MyComponent = React.memo(({ name }) => <Text>{name}</Text>, (prev, next) => prev.name === next.name);
  • korzystaj z useCallback i useMemo, unikaj anonimowych funkcji w propsach:
import React, { useCallback } from 'react';
import { Button } from 'react-native';

const MyScreen = () => {
  const handlePress = useCallback(() => {
    console.log('Kliknięto!');
  }, []);

  return <Button onPress={handlePress} title="Kliknij mnie" />;
};

Oprócz React.memo czy useCallback, pamiętaj o przenoszeniu stanu (lifting state up). Technia lifting state up czyli przenoszenie stanu do wspólnego rodzica komponentów, zamiast trzymania go lokalnie w wielu mniejszych komponentach React Native. Kiedy stan jest zarządzany na niższym poziomie (w wielu osobnych komponentach), każda zmiana powoduje często wielokrotne, niepotrzebne rerendery komponentów potomnych. Przeniesienie stanu wyżej (do komponentu-rodzica):

  • pozwala efektywnie zarządzać zmianami stanu i propagować go tylko tam, gdzie jest to faktycznie konieczne.
  • ogranicza liczbę komponentów, które reagują na zmiany (renderują się tylko te, które naprawdę potrzebują dostępu do stanu),
  • poprawia czytelność kodu oraz zarządzanie stanem,

Przykład:
Zamiast mieć stan lokalny w każdym elemencie listy, zarządzaj stanem w komponencie nadrzędnym (np. przechowuj zaznaczone elementy w rodzicu, a nie osobno w każdym komponencie). Dzięki temu mniejsze komponenty będą rerenderowane tylko wtedy, kiedy zmieni się ich konkretny stan.

// Przykładowy lifting state up
const ParentComponent = () => {
  const [selectedItem, setSelectedItem] = useState(null);

  return (
    <>
      <ChildComponent
        selected={selectedItem === 'item1'}
        onSelect={() => setSelectedItem('item1')}
      />
      <ChildComponent
        selected={selectedItem === 'item2'}
        onSelect={() => setSelectedItem('item2')}
      />
    </>
  );
};
Szukasz wykonawcy dla swoich projektów IT?
Szukasz wykonawcy dla swoich projektów IT?
Szukasz wykonawcy dla swoich projektów IT?
Napisz do nas!

3. Optymalizacja obrazów

Obrazy znacząco wpływają na szybkość aplikacji. Zawsze:

  • dobieraj format (JPEG, PNG lub WebP) odpowiednio do typu grafik.
  • korzystaj z biblioteki react-native-fast-image, która umożliwia efektywne cache’owanie:
import FastImage from 'react-native-fast-image';

const MyImage = () => (
  <FastImage
    source={{ uri: 'https://example.com/image.png' }}
    resizeMode={FastImage.resizeMode.cover}
    style={{ width: 200, height: 200 }}
  />
);

4. Zarządzanie długimi listami – dlaczego długie listy mogą powodować problemy z wydajnością?

Wyświetlanie dużej liczby elementów jednocześnie może negatywnie wpływać na wydajność, ponieważ:

  • Każdy element renderowany w React Native konsumuje zasoby CPU i RAM.
  • Renderowanie wielu elementów jednocześnie prowadzi do długiego czasu pierwszego renderu oraz „lagi” podczas przewijania listy.
  • Nieoptymalizowana lista może prowadzić do częstych re-renderów niepotrzebnych elementów.

Jak optymalizować długie listy w React Native?

1. Używaj FlatList zamiast ScrollViewFlatList to zoptymalizowany komponent wbudowany w React Native, który renderuje tylko elementy aktualnie widoczne na ekranie. Przykład użycia FlatList:

<FlatList
  data={data}
  keyExtractor={(item) => item.id.toString()}
  renderItem={({ item }) => <ListItem item={item} />}
/>

Zalety:

  • Automatycznie renderuje tylko elementy widoczne na ekranie („windowing”).
  • Oszczędza pamięć i czas CPU.

2. Implementuj klucze unikalne (keyExtractor) – klucze pomagają Reactowi prawidłowo identyfikować elementy i efektywnie aktualizować interfejs użytkownika. Dobrą praktyką jest unikanie używania indeksów tablicy jako kluczy, ponieważ powodują one niepotrzebne re-rendery przy zmianach danych. Przykład:

<FlatList
  data={data}
  keyExtractor={(item) => item.uniqueId}
/>

3. Stosuj optymalizację getItemLayout – metoda getItemLayout umożliwia FlatList szybsze obliczenie rozmiarów elementów, co wpływa na szybkość renderowania. Przykład:

<FlatList
  data={data}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
/>

4. Wykorzystaj komponenty PureComponent lub React.memo do optymalizacji elementów listy – Każdy element listy powinien być optymalizowany osobno, aby uniknąć niepotrzebnych renderów podczas przewijania. Przykład z React.memo:

const ListItem = React.memo(({ item }) => (
  <View>
    <Text>{item.name}</Text>
  </View>
));

5. Ograniczaj liczbę renderowanych elementów (initialNumToRender) – domyślne wartości często nie są optymalne. Możesz dostosować parametry FlatList do swoich potrzeb:

  • initialNumToRender – liczba elementów renderowanych na starcie.
  • windowSize – liczba ekranów renderowanych poza widokiem.
<FlatList
  initialNumToRender={10}
  windowSize={5}
/>

6. Optymalizuj obrazy w elementach listy – używaj bibliotek do optymalnego ładowania obrazów np. react-native-fast-image zamiast standardowego komponentu Image. Przykład:

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

<FastImage
  style={{ width: 100, height: 100 }}
  source={{
    uri: 'https://example.com/image.png',
    priority: FastImage.priority.normal,
  }}
  resizeMode={FastImage.resizeMode.cover}
/>

Podsumowanie optymalizacji długich list w React Native:

  • Zawsze stosuj FlatList lub SectionList.
  • Używaj unikalnych kluczy.
  • Implementuj getItemLayout.
  • Optymalizuj komponenty elementów listy (React.memo, PureComponent).
  • Zarządzaj właściwościami FlatList (np. initialNumToRender, windowSize).
  • Optymalizuj obrazy (react-native-fast-image).
  • Stosuj lazy loading danych przy dużych zestawach informacji.

5. Ciężkie obliczenia poza głównym wątkiem

Aby uniknąć blokowania interfejsu użytkownika:

  • wykorzystaj InteractionManager do przesunięcia operacji na czas po interakcjach:
import { InteractionManager } from 'react-native';

InteractionManager.runAfterInteractions(() => {
  // Wykonaj czasochłonne operacje
});

6. Równoważenie animacji między wątkiem JavaScript a głównym wątkiem

Animacje w React Native mogą być obsługiwane przez dwa główne wątki:

  • JavaScript Thread – wykonuje logikę aplikacji (obliczenia, operacje stanu).
  • UI (Główny) Thread – obsługuje renderowanie interfejsu użytkownika.

Domyślnie animacje wykonywane za pomocą metody setState są realizowane na wątku JavaScript, co może powodować opóźnienia i spadki wydajności, szczególnie gdy JavaScript jest intensywnie obciążony innymi zadaniami.

Jak optymalizować animacje w React Native?

Używaj natywnych animacji

React Native oferuje API Animated, które pozwala na przeniesienie animacji do wątku natywnego. Warto ustawić useNativeDriver: true, co spowoduje wykonanie animacji poza wątkiem JavaScript:

Animated.timing(animationValue, {
  toValue: 1,
  duration: 300,
  useNativeDriver: true,
}).start();

InteractionManager

Czasami nawet natywne animacje mogą być zakłócane przez intensywną logikę JavaScript. W takim przypadku użyj InteractionManager.runAfterInteractions do odłożenia ciężkich obliczeń lub pobierania danych na moment po zakończeniu animacji:

InteractionManager.runAfterInteractions(() => {
  // Ciężkie operacje, np. pobieranie danych
});

7. Cache’owanie danych

Ogranicz liczbę zapytań do serwera przez efektywne cache’owanie danych:

import { useQuery } from 'react-query';

const { data, isLoading } = useQuery('items', fetchItems, {
  staleTime: 120000, // cache danych na 2 minuty
});

8. Analiza i zmniejszanie rozmiaru paczki (bundle size) – dlaczego rozmiar paczki jest ważny?

Jak efektywnie analizować i zmniejszać rozmiar paczki w React Native? Poniżej najważniejsze wskazówki z przykładami:

1. React Native Bundle Visualizer

Wykorzystaj bibliotekę react-native-bundle-visualizer:

  • wizualizuje rozmiar każdego modułu aplikacji.
  • pozwala zidentyfikować największe biblioteki.

2. Stosuj „code splitting” i dynamiczne importy – zastosowanie dynamicznych importów pozwala ładować fragmenty aplikacji dopiero, gdy są potrzebne.

Przykład dynamicznego importu:

const LazyComponent = React.lazy(() => import('./HeavyComponent'));

const App = () => (
  <Suspense fallback={<Text>Ładowanie...</Text>}>
    <LazyComponent />
  </Suspense>
);

3. Usuwaj zbędne biblioteki oraz martwy kod – regularnie weryfikuj użycie bibliotek i usuń nieużywane moduły. Wykorzystuj narzędzia takie jak ESLint, które wskazują nieużywane fragmenty kodu.

4. Optymalizuj zasoby multimedialne – kompresuj obrazy za pomocą narzędzi takich jak imagemin czy TinyPNG. Wybieraj zoptymalizowane formaty grafik: WebP lub SVG.

5. Tree Shaking – upewnij się, że używasz bundlera obsługującego tree shaking (np. Metro z odpowiednią konfiguracją, Hermes lub dodatkowe narzędzia, jak Metro bundler w trybie produkcyjnym). Importuj tylko potrzebne elementy:

// unikaj tego:
import _ from 'lodash';

// Zamiast tego:
import { isEmpty } from 'lodash';

9. Testowanie na rzeczywistych urządzeniach

Zawsze testuj wydajność na prawdziwych urządzeniach, nie tylko emulatorach czy symulatorach. Tylko testy na fizycznych urządzeniach dają wiarygodne informacje o realnym zachowaniu aplikacji.

Jak AI może pomóc w optymalizacji wydajności React Native?

Jak AI może realnie pomóc w wykrywaniu problemów wydajnościowych? Oto praktyczne sposoby, jak AI może realnie wspierać rozwój Twojej aplikacji React Native:

Przykładowa sytuacja: Zauważasz spowolnienie w swojej aplikacji, ale nie wiesz, gdzie jest źródło problemu. Wystarczy wkleić fragment kodu do narzędzia opartego o AI (np. chatGPT) z promptem:

„Pokaż, które fragmenty poniższego kodu React Native mogą powodować problemy z wydajnością i zaproponuj ulepszenia.”

Inne przykładowe prompty, które możesz wykorzystać u siebie:

„Jak mogę zaimplementować predykcyjne ładowanie danych w mojej aplikacji React Native, aby poprawić szybkość wczytywania treści użytkownikom?”

„Na podstawie poniższego pliku package.json oraz struktury mojego projektu React Native, jakie zmiany mogę wprowadzić, aby skutecznie zredukować rozmiar paczki aplikacji?”

Kilka obszarów w których AI pomoże Ci w pracy nad optymalizacją wydajności aplikacji React Native:

1. Inteligentna analiza kodu na bieżąco

AI może na bieżąco analizować twój kod React Native i natychmiast sugerować usprawnienia, wskazywać problemy wydajnościowe czy eliminować zbędne renderowanie komponentów.

2. Automatyczne wykrywanie wąskich gardeł

Dzięki monitorowaniu wydajności aplikacji w czasie rzeczywistym, narzędzia AI mogą szybko identyfikować miejsca powodujące największe obciążenia oraz dostarczać rekomendacje ich rozwiązania.

3. Personalizacja UX/UI na podstawie analizy zachowań użytkowników

AI analizuje zachowania użytkowników aplikacji, sugerując dostosowanie interfejsu oraz funkcjonalności, by zapewnić szybsze i bardziej intuicyjne działanie aplikacji.

4. Predykcyjne zarządzanie zasobami

AI potrafi przewidywać, które zasoby (obrazy, dane, komponenty) użytkownik będzie potrzebował za chwilę, dzięki czemu aplikacja może wcześniej je załadować i tym samym zwiększyć jej responsywność.

5. Inteligentne alertowanie o spadkach wydajności

AI może dynamicznie identyfikować nietypowe zachowania aplikacji, automatycznie wysyłając powiadomienia zespołowi deweloperskiemu, co pozwala na błyskawiczną reakcję i minimalizację skutków problemów.

Powiązane artykuły
Jak poprawić wydajność aplikacji React Native?
8 min
17 marca, 2025
Flutter - świetne narzędzie do tworzenia aplikacji na Androida oraz iOS
Flutter to innowacyjne narzędzie programistyczne od Google, które umożliwia szybkie i efektywne tworzenie aplikacji mobilnych...
Dowiedz się więcej
Jak poprawić wydajność aplikacji React Native?
10 min
13 marca, 2025
NativeWind czyli jak używać Tailwind CSS w React Native?
W React Native tradycyjne metody, jak StyleSheet czy Styled Components, często prowadzą do nadmiernie rozbudowanego...
Dowiedz się więcej
Jak poprawić wydajność aplikacji React Native?
4 min
12 marca, 2025
Lynx – nowy gracz na rynku cross-platformowych frameworków mobilnych
Jednym z najnowszych narzędzi, które pojawiło się w ostatnim czasie i które zdobywa coraz większą...
Dowiedz się więcej
Zobacz wszystkie
Odkryj więcej tematów