Zašto je optimizacija performansi u React Nativeu važnija nego ikad
Priznajmo si — React Native u 2026. je potpuno drugačija zvijer od onog frameworka koji smo koristili prije dvije-tri godine. Nova arhitektura s JSI-jem, Fabricom i TurboModulima sada je standard, Hermes je jedini podržani JavaScript engine, a alati za optimizaciju su se nevjerojatno poboljšali. Ali evo zanimljive stvari: čak i uz sve te napretke, loše optimizirana aplikacija i dalje može biti frustrirajuće spora.
Dva mjerila definiraju performanse svake React Native aplikacije: Time to Interactive (TTI) — koliko brzo korisnik može početi koristiti aplikaciju nakon pokretanja, i Frames Per Second (FPS) — koliko glatko se sve prikazuje tijekom korištenja. Cilj je 60 FPS (ili čak 120 FPS na uređajima s bržim ekranima) i što kraći startup.
U ovom vodiču proći ćemo kroz sve bitne aspekte optimizacije — od listi i animacija, preko upravljanja stanjem i slikama, do konkretnih tehnika profiliranja. Svaki dio dolazi s primjerima koda koje možete odmah primijeniti.
Hermes engine: Temelj brzog pokretanja aplikacije
Hermes je JavaScript engine koji je Meta razvila specifično za React Native. Od verzije 0.82 i Expo SDK 55, Hermes je zadani i jedini podržani engine. Ali čak i ako ga već koristite — a gotovo sigurno ga koristite — razumijevanje kako Hermes radi pod haubom pomoći će vam u donošenju boljih odluka.
Kako Hermes ubrzava pokretanje
Hermes koristi ahead-of-time (AOT) kompilaciju koja pretvara JavaScript u optimizirani bytecode tijekom builda. To znači da se pri pokretanju aplikacije ne mora ništa parsirati ni kompilirati — bytecode je spreman odmah. Rezultati su, iskreno, prilično impresivni:
- Vrijeme pokretanja — smanjenje od otprilike 55% u usporedbi s JavaScriptCore (npr. s 4.5 sekundi na 2.0 sekunde)
- Potrošnja memorije — značajno manja zahvaljujući optimiziranom garbage collectoru
- Veličina aplikacije — bytecode zauzima manje prostora, ušteda od 1-2 MB bez ikakvih promjena u kodu
Praktični savjeti za maksimalno iskorištavanje Hermesa
Iako Hermes radi automatski, ima par stvari koje možete učiniti da izvučete maksimum:
// 1. Izbjegavajte eval() i new Function() — Hermes ne može
// AOT kompilirati dinamički generirani kod
// LOŠE:
const compute = new Function('a', 'b', 'return a + b');
// DOBRO:
function compute(a: number, b: number): number {
return a + b;
}
// 2. Koristite TypeScript stroge tipove — pomaže Hermesu
// u optimizaciji bytecoda
// LOŠE:
let value: any = getData();
// DOBRO:
let value: number = getData();
// 3. Smanjite veličinu JavaScript bundlea
// metro.config.js
module.exports = {
transformer: {
minifierConfig: {
compress: {
drop_console: true, // Ukloni console.log u produkciji
dead_code: true,
},
},
},
};
Renderiranje listi: FlashList v2 umjesto FlatList
Liste su, po mom iskustvu, najčešći uzrok performansnih problema u React Native aplikacijama. Standardna FlatList koristi virtualizaciju — prikazuje samo vidljive stavke. Ali kod složenijih stavki i velikih datasetova, FlatList jednostavno ne drži korak. Tu dolazi FlashList v2.
Zašto je FlashList v2 brži
FlashList (Shopifyjev projekt) koristi fundamentalno drugačiji pristup — recikliranje ćelija umjesto klasične virtualizacije. Umjesto da uništava i ponovo stvara komponente kad stavka izađe iz vidljivog područja, FlashList drži fiksni pool instanci u memoriji i samo im mijenja podatke. Razlika u praksi je dramatična.
FlashList v2 je potpuno prepisan za novu arhitekturu i donosi dodatna poboljšanja:
- Automatsko određivanje veličine stavki — za razliku od v1, više ne morate ručno specificirati
estimatedItemSize - Do 10x brže renderiranje u usporedbi s FlatList
- Stabilan 60 FPS čak i sa složenim komponentama
- Glatko skrolanje s tisućama stavki bez zastajkivanja
Migracija s FlatList na FlashList
Dobra vijest — FlashList je dizajniran kao drop-in replacement. Migracija je gotovo trivijalna:
// PRIJE: Korištenje FlatList
import { FlatList } from 'react-native';
function ProductList({ products }) {
return (
<FlatList
data={products}
renderItem={({ item }) => <ProductCard product={item} />}
keyExtractor={(item) => item.id}
/>
);
}
// POSLIJE: Korištenje FlashList v2
import { FlashList } from '@shopify/flash-list';
function ProductList({ products }) {
return (
<FlashList
data={products}
renderItem={({ item }) => <ProductCard product={item} />}
keyExtractor={(item) => item.id}
estimatedItemSize={120} // opcionalno u v2, ali preporučeno
/>
);
}
Napredne optimizacije FlashList-a
Za maksimalne performanse, vrijedi primijeniti ove tehnike:
import { FlashList } from '@shopify/flash-list';
import React, { useCallback, memo } from 'react';
// 1. Memorizirajte renderItem komponentu
const ProductCard = memo(({ product }) => (
<View style={styles.card}>
<Text>{product.name}</Text>
<Text>{product.price} EUR</Text>
</View>
));
function OptimizedProductList({ products }) {
// 2. Koristite useCallback za renderItem
const renderItem = useCallback(({ item }) => (
<ProductCard product={item} />
), []);
// 3. Koristite getItemType za heterogene liste
const getItemType = useCallback((item) => {
return item.isPromo ? 'promo' : 'regular';
}, []);
return (
<FlashList
data={products}
renderItem={renderItem}
keyExtractor={(item) => item.id}
estimatedItemSize={120}
getItemType={getItemType}
// 4. drawDistance za preloading
drawDistance={300}
/>
);
}
Animacije s Reanimated 4: CSS sintaksa na UI niti
Animacije su ključni dio korisničkog iskustva. Loše implementirane animacije ubijaju FPS, uzrokuju zastajkivanja i vizualne artefakte. React Native Reanimated 4 donosi zaista veliku promjenu — uvodi CSS-kompatibilni API koji radi izravno na UI niti.
Što je novo u Reanimated 4
Reanimated 4 je stigao sa stabilnim izdanjem i donosi stvari na koje smo dugo čekali:
- CSS-kompatibilni animacijski API — poznata web sintaksa (keyframes, transitions) koja se izvršava na nativnoj UI niti s 60+ FPS
- Worklets u zasebnom paketu —
react-native-worklets-coreomogućuje i drugim bibliotekama korištenje te funkcionalnosti - Shared Element Transitions (od verzije 4.2) — animirani prijelazi elemenata između ekrana
- 3x bolje performanse na složenim aplikacijama u usporedbi s prethodnim verzijama
- Samo nova arhitektura — potpuna integracija s Fabricom i JSI-jem
CSS animacije u Reanimated 4
Novi CSS API dramatično pojednostavljuje stvaranje animacija. Pogledajte ovo:
import Animated, {
useAnimatedStyle,
withTiming,
withSpring,
FadeIn,
FadeOut,
SlideInRight,
} from 'react-native-reanimated';
// 1. Jednostavne ulazne/izlazne animacije
function AnimatedCard({ children }) {
return (
<Animated.View
entering={FadeIn.duration(400).springify()}
exiting={FadeOut.duration(300)}
>
{children}
</Animated.View>
);
}
// 2. CSS-stil keyframe animacije
import { Keyframe } from 'react-native-reanimated';
const bounceKeyframe = new Keyframe({
0: {
transform: [{ scale: 0 }],
opacity: 0,
},
50: {
transform: [{ scale: 1.2 }],
opacity: 0.8,
},
100: {
transform: [{ scale: 1 }],
opacity: 1,
},
});
function BouncingElement() {
return (
<Animated.View entering={bounceKeyframe.duration(600)}>
<Text>Pozdrav!</Text>
</Animated.View>
);
}
// 3. Animirani stilovi s useAnimatedStyle
import { useSharedValue } from 'react-native-reanimated';
function SliderComponent() {
const offset = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateX: withSpring(offset.value) }],
}));
return (
<Animated.View style={[styles.box, animatedStyle]} />
);
}
Shared Element Transitions
Jedna od najočekivanijih značajki Reanimated 4.2 — dijeljeni prijelazi elemenata između ekrana. Iskreno, ovo je nešto što sam oduvijek htio imati u React Nativeu:
import Animated from 'react-native-reanimated';
import { SharedTransition } from 'react-native-reanimated';
// Na prvom ekranu (lista proizvoda)
function ProductListScreen({ navigation }) {
return (
<TouchableOpacity onPress={() => navigation.navigate('Detail', { id: product.id })}>
<Animated.Image
source={{ uri: product.imageUrl }}
sharedTransitionTag={`product-image-${product.id}`}
style={styles.thumbnail}
/>
<Text>{product.name}</Text>
</TouchableOpacity>
);
}
// Na drugom ekranu (detalji proizvoda)
function ProductDetailScreen({ route }) {
const { id } = route.params;
return (
<View>
<Animated.Image
source={{ uri: product.imageUrl }}
sharedTransitionTag={`product-image-${id}`}
style={styles.heroImage}
/>
<Text>{product.description}</Text>
</View>
);
}
Optimizacija slika: expo-image i napredne strategije
Slike su često najveći potrošač memorije u mobilnim aplikacijama. Standardna Image komponenta nema optimalno keširanje, nema podršku za progresivno učitavanje, a zna prouzročiti ozbiljne skokove u potrošnji memorije. Za 2026., preporučam expo-image za Expo projekte i react-native-fast-image za bare RN projekte.
expo-image: Moderna zamjena za Image
expo-image automatski smanjuje rezoluciju slike na veličinu spremnika, što značajno štedi memoriju. Evo kako je koristiti:
import { Image } from 'expo-image';
// Osnovna uporaba s blurhash placeholderom
function OptimizedImage({ uri }) {
return (
<Image
source={{ uri }}
style={{ width: 300, height: 200 }}
contentFit="cover"
placeholder={{ blurhash: 'LGF5?xYk^6#M@-5c,1J5@[or[Q6.' }}
transition={300}
cachePolicy="memory-disk"
/>
);
}
// Preloading slika za brže prikazivanje
import { Image } from 'expo-image';
async function preloadImages(urls: string[]) {
await Image.prefetch(urls);
}
// Pozovite pri pokretanju ili prije navigacije
useEffect(() => {
const imageUrls = products.map(p => p.imageUrl);
preloadImages(imageUrls);
}, [products]);
Strategije optimizacije slika
Bez obzira na biblioteku, ove strategije vrijedi primijeniti uvijek:
- Koristite WebP format — do 30% manji od JPEG-a uz istu kvalitetu
- Ispravne dimenzije — nemojte slati sliku od 3000x2000 piksela za prikaz u spremniku od 300x200. Koristite serversko skaliranje ili CDN
- Lazy loading — učitavajte slike tek kad su blizu vidljivog područja
- SVG za ikone — lakše su, skaliraju se bez gubitka kvalitete
// Primjer korištenja CDN-a za optimizirane slike
function getOptimizedImageUrl(
originalUrl: string,
width: number,
height: number
): string {
// Primjer za Cloudinary CDN
return originalUrl.replace(
'/upload/',
`/upload/w_${width},h_${height},c_fill,f_webp,q_auto/`
);
}
// Korištenje
<Image
source={{ uri: getOptimizedImageUrl(product.imageUrl, 300, 200) }}
style={{ width: 300, height: 200 }}
contentFit="cover"
/>
React.memo, useMemo i useCallback: Ispravna uporaba
Memoizacija je jedan od najčešće korištenih, ali — budimo iskreni — i najčešće pogrešno korištenih alata za optimizaciju. Razumijevanje kada i zašto koristiti React.memo, useMemo i useCallback je ključno za performantan kod bez nepotrebne složenosti.
Kada koristiti React.memo
React.memo sprječava ponovno renderiranje komponente ako se propsi nisu promijenili. Koristite je za:
- Komponente koje primaju iste propse, ali im se roditelj često renderira
- Stavke lista (renderItem u FlashList/FlatList)
- Složene komponente čije renderiranje je skupo
// KADA koristiti React.memo — složena komponenta u listi
const ExpensiveListItem = memo(({ item, onPress }) => {
return (
<TouchableOpacity onPress={() => onPress(item.id)}>
<View style={styles.card}>
<Image source={{ uri: item.image }} style={styles.image} />
<View style={styles.details}>
<Text style={styles.title}>{item.name}</Text>
<PriceDisplay price={item.price} currency={item.currency} />
<RatingStars rating={item.rating} />
</View>
</View>
</TouchableOpacity>
);
});
// KADA NE koristiti React.memo — jednostavna komponenta
// Trošak usporedbe propsa može biti veći od renderiranja
function SimpleLabel({ text }) {
return <Text>{text}</Text>;
}
useMemo vs useCallback
useMemo memorira izračunate vrijednosti, a useCallback memorira funkcije. Oba sprječavaju nepotrebne ponovne izračune pri svakom renderiranju.
import { useMemo, useCallback, useState } from 'react';
function ProductFilterScreen({ products }) {
const [searchQuery, setSearchQuery] = useState('');
const [sortBy, setSortBy] = useState('name');
// useMemo — skupo filtriranje i sortiranje
const filteredProducts = useMemo(() => {
console.log('Filtriranje i sortiranje...');
return products
.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()))
.sort((a, b) => {
if (sortBy === 'name') return a.name.localeCompare(b.name);
if (sortBy === 'price') return a.price - b.price;
return 0;
});
}, [products, searchQuery, sortBy]);
// useCallback — stabilna referenca na funkciju
const handleProductPress = useCallback((productId: string) => {
navigation.navigate('ProductDetail', { id: productId });
}, [navigation]);
return (
<View>
<TextInput
value={searchQuery}
onChangeText={setSearchQuery}
placeholder="Pretraži proizvode..."
/>
<FlashList
data={filteredProducts}
renderItem={({ item }) => (
<ExpensiveListItem item={item} onPress={handleProductPress} />
)}
estimatedItemSize={120}
/>
</View>
);
}
React Compiler: Automatska memoizacija
Velika novost je React Compiler koji automatski memoizira vrijednosti i funkcije. Ako koristite React 19 (dostupan s Expo SDK 53), svakako razmotrite njegovo uključivanje:
// babel.config.js — omogućavanje React Compilera
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
['babel-plugin-react-compiler', {
// Konfiguracija compilera
}],
],
};
};
S React Compilerom, mnoge ručne optimizacije postaju nepotrebne. Compiler sam prepoznaje što treba memoizirati i dodaje optimizacije tijekom kompilacije. Zaista korisno.
Upravljanje stanjem: Utjecaj na performanse
Izbor state management biblioteke ima izravan utjecaj na performanse. Svaka promjena stanja pokreće re-render, a neefikasno upravljanje stanjem može dovesti do renderiranja stotina komponenata bez razloga.
Zustand: Minimalistički i brz
Zustand je u 2026. najkorištenija biblioteka za state management u React Native projektima — i to s razlogom. Sa samo 3 KB, praktički nema utjecaj na bundle, a performanse su izvrsne zahvaljujući selektivnoj pretplati:
import { create } from 'zustand';
// Definiranje storea
interface CartStore {
items: CartItem[];
total: number;
addItem: (item: CartItem) => void;
removeItem: (itemId: string) => void;
clearCart: () => void;
}
const useCartStore = create<CartStore>((set, get) => ({
items: [],
total: 0,
addItem: (item) => set((state) => {
const newItems = [...state.items, item];
return {
items: newItems,
total: newItems.reduce((sum, i) => sum + i.price, 0),
};
}),
removeItem: (itemId) => set((state) => {
const newItems = state.items.filter(i => i.id !== itemId);
return {
items: newItems,
total: newItems.reduce((sum, i) => sum + i.price, 0),
};
}),
clearCart: () => set({ items: [], total: 0 }),
}));
// KLJUČNO: Selektivna pretplata
// Komponenta se renderira SAMO kad se promijeni njen podatak
function CartBadge() {
const itemCount = useCartStore((state) => state.items.length);
return <Badge count={itemCount} />;
}
function CartTotal() {
const total = useCartStore((state) => state.total);
return <Text>Ukupno: {total} EUR</Text>;
}
Jotai: Atomički pristup za složene međuovisnosti
Jotai koristi atomički pristup — svaki dio stanja je zaseban atom koji se može kombinirati s drugima. Ovo je posebno korisno kad imate složene međuovisnosti:
import { atom, useAtom, useAtomValue } from 'jotai';
// Bazni atomi
const productsAtom = atom<Product[]>([]);
const searchQueryAtom = atom('');
const selectedCategoryAtom = atom<string | null>(null);
// Izvedeni atom — automatski se ažurira kad se
// promijeni bilo koji bazni atom
const filteredProductsAtom = atom((get) => {
const products = get(productsAtom);
const query = get(searchQueryAtom);
const category = get(selectedCategoryAtom);
return products.filter(p => {
const matchesQuery = p.name.toLowerCase()
.includes(query.toLowerCase());
const matchesCategory = !category || p.category === category;
return matchesQuery && matchesCategory;
});
});
// Komponenta se renderira SAMO kad se promijeni
// rezultat filtriranja
function FilteredProductList() {
const products = useAtomValue(filteredProductsAtom);
return (
<FlashList
data={products}
renderItem={({ item }) => <ProductCard product={item} />}
estimatedItemSize={120}
/>
);
}
Kada koristiti što
Evo kratkog pregleda za odabir:
- Zustand — za većinu aplikacija. Jednostavan, brz, fleksibilan. Odličan za globalno stanje poput auth-a, postavki i API podataka
- Jotai — za aplikacije sa složenim međuovisnostima stanja. Sjajan za forme, filtere i konfigurabilna sučelja
- React Context — samo za rijetko mijenjane podatke (tema, lokalizacija). Nikada za često ažurirane podatke jer svaka promjena renderira sve potrošače
Izbjegavanje nepotrebnog ponovnog renderiranja
Nepotrebno re-renderiranje je, po mom iskustvu, najčešći uzrok performansnih problema u React Native aplikacijama. Hajde da prođemo kroz najčešće zamke.
Zamka 1: Inline funkcije i objekti u propsima
// LOŠE: Stvara novu funkciju i objekt stila svaki put
function ParentComponent() {
return (
<ChildComponent
onPress={() => console.log('pressed')}
style={{ marginTop: 10, padding: 5 }}
/>
);
}
// DOBRO: Stabilne reference
const styles = StyleSheet.create({
child: { marginTop: 10, padding: 5 },
});
function ParentComponent() {
const handlePress = useCallback(() => {
console.log('pressed');
}, []);
return (
<ChildComponent
onPress={handlePress}
style={styles.child}
/>
);
}
Zamka 2: Neoptimalno korištenje konteksta
// LOŠE: Jedan kontekst s mnogo podataka
const AppContext = createContext({
user: null,
theme: 'light',
notifications: [],
cart: [],
});
// Bilo koja promjena u cart[] renderira SVE komponente
// koje koriste AppContext — čak i one koje trebaju samo theme
// DOBRO: Razdvojite kontekste po učestalosti promjena
const ThemeContext = createContext('light');
const UserContext = createContext(null);
const CartContext = createContext<CartItem[]>([]);
// Ili još bolje — koristite Zustand/Jotai za često
// mijenjane podatke
Zamka 3: Ključevi listi
// LOŠE: Korištenje indeksa kao ključa
{items.map((item, index) => (
<ItemComponent key={index} item={item} />
))}
// DOBRO: Korištenje stabilnog, jedinstvenog identifikatora
{items.map((item) => (
<ItemComponent key={item.id} item={item} />
))}
Profiliranje i dijagnostika performansi
Optimizacija bez mjerenja je pogađanje — i to sam naučio na teži način. React Native u 2026. nudi nekoliko odličnih alata za profiliranje. Flipper je zamijenjen novim React Native DevTools alatom koji podržava sve Hermes aplikacije.
React Native DevTools
React Native DevTools su službeni alat koji zamjenjuje Flipper. Pokretanje je jednostavno:
# Pokretanje React Native DevTools
npx react-native start
# DevTools se automatski otvaraju u pregledniku
# Ili ručno — u terminalu pritisnite 'j'
DevTools nude:
- React Components Inspector — pregled stabla komponenata, propsa i stanja
- Profiler — mjerenje vremena renderiranja svake komponente
- Network Inspector — praćenje API poziva
- Console — JavaScript konzola s naprednim filtriranjem
Korištenje Performance API-ja
Za precizno mjerenje specifičnih operacija, performance API je nezamjenjiv:
import { PerformanceObserver, performance } from 'react-native-performance';
// Mjerenje vremena renderiranja ekrana
function ProductListScreen() {
useEffect(() => {
performance.mark('productList-start');
return () => {
performance.mark('productList-end');
performance.measure(
'productList-render',
'productList-start',
'productList-end'
);
const measures = performance.getEntriesByName('productList-render');
console.log(`Vrijeme renderiranja: ${measures[0]?.duration}ms`);
};
}, []);
// ... ostatak komponente
}
// Praćenje sporih operacija
async function fetchProducts() {
performance.mark('fetch-start');
const response = await fetch('https://api.example.com/products');
const data = await response.json();
performance.mark('fetch-end');
performance.measure('fetch-products', 'fetch-start', 'fetch-end');
const [measure] = performance.getEntriesByName('fetch-products');
if (measure.duration > 2000) {
console.warn(`Spor API poziv: ${measure.duration}ms`);
}
return data;
}
Praćenje FPS-a u razvoju
Za kontinuirano praćenje FPS-a tijekom razvoja, ovo je super korisno:
import { useFrameRate } from 'react-native-performance';
function FPSMonitor() {
const { fps, jsFps, uiFps } = useFrameRate();
if (__DEV__) {
return (
<View style={styles.fpsOverlay}>
<Text style={styles.fpsText}>JS: {jsFps} FPS</Text>
<Text style={styles.fpsText}>UI: {uiFps} FPS</Text>
</View>
);
}
return null;
}
const styles = StyleSheet.create({
fpsOverlay: {
position: 'absolute',
top: 50,
right: 10,
backgroundColor: 'rgba(0,0,0,0.7)',
padding: 8,
borderRadius: 4,
zIndex: 9999,
},
fpsText: {
color: '#00ff00',
fontSize: 12,
fontFamily: 'monospace',
},
});
Upravljanje memorijom i čišćenje resursa
Memory leakovi su jedan od najopasnijih problema u mobilnim aplikacijama jer postupno degradiraju performanse dok se aplikacija ne sruši. Evo najčešćih izvora i kako ih spriječiti.
Čišćenje pretplata i slušatelja
import { useEffect } from 'react';
import { AppState, Keyboard } from 'react-native';
function MyScreen() {
useEffect(() => {
// Pretplata na promjene stanja aplikacije
const appStateSubscription = AppState.addEventListener(
'change',
handleAppStateChange
);
// Pretplata na događaje tipkovnice
const keyboardDidShow = Keyboard.addListener(
'keyboardDidShow',
handleKeyboardShow
);
// WebSocket veza
const ws = new WebSocket('wss://api.example.com/ws');
ws.onmessage = handleMessage;
// KLJUČNO: Čišćenje pri unmount-u
return () => {
appStateSubscription.remove();
keyboardDidShow.remove();
ws.close();
};
}, []);
// ...
}
// Abort controller za API pozive
function useProducts() {
const [products, setProducts] = useState([]);
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
const response = await fetch('https://api.example.com/products', {
signal: controller.signal,
});
const data = await response.json();
setProducts(data);
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Greška pri dohvaćanju:', error);
}
}
}
fetchData();
return () => controller.abort();
}, []);
return products;
}
Optimizacija navigacije
React Navigation je standard za navigaciju u React Native aplikacijama. Ali neoptimizirani navigacijski sustav može ozbiljno usporiti aplikaciju — pogotovo ako se svi ekrani renderiraju unaprijed.
Lijeno učitavanje ekrana
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import React, { lazy, Suspense } from 'react';
import { ActivityIndicator } from 'react-native';
// Lijeno učitavanje ekrana
const ProductDetailScreen = lazy(() =>
import('./screens/ProductDetailScreen')
);
const CheckoutScreen = lazy(() =>
import('./screens/CheckoutScreen')
);
const ProfileScreen = lazy(() =>
import('./screens/ProfileScreen')
);
const Stack = createNativeStackNavigator();
function AppNavigator() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="ProductDetail">
{(props) => (
<Suspense fallback={<ActivityIndicator />}>
<ProductDetailScreen {...props} />
</Suspense>
)}
</Stack.Screen>
<Stack.Screen name="Checkout">
{(props) => (
<Suspense fallback={<ActivityIndicator />}>
<CheckoutScreen {...props} />
</Suspense>
)}
</Stack.Screen>
</Stack.Navigator>
);
}
Smrzavanje neaktivnih ekrana
import { enableFreeze } from 'react-native-screens';
// Omogućite zamrzavanje neaktivnih ekrana
enableFreeze(true);
// U navigatoru
<Tab.Navigator
screenOptions={{
lazy: true,
freezeOnBlur: true,
}}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Search" component={SearchScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
Kontrolna lista za optimizaciju: Prije objave aplikacije
Prije nego objavite na App Store ili Google Play, prođite kroz ovu listu (ozbiljno, isprintajte ju):
- Bundle veličina — Uklonite nekorištene pakete, omogućite tree shaking, maknite console.log iz produkcije
- Slike — Sve u WebP formatu, ispravnih dimenzija, s keširanjem
- Liste — FlashList umjesto FlatList za liste s više od 50 stavki
- Animacije — Sve koriste Reanimated ili nativni driver, nijedna ne blokira JS nit
- Stanje — Nema nepotrebnih renderiranja, konteksti razdvojeni po učestalosti promjena
- Memorija — Sve pretplate se čiste, API pozivi koriste AbortController
- Navigacija — Neaktivni ekrani zamrznuti, težak sadržaj se učitava lijeno
- Profiliranje — Testirajte na stvarnom uređaju (ne emulatoru!), posebno na slabijim Android uređajima
Zaključak
Optimizacija performansi React Native aplikacije nije nešto što napravite jednom i zaboravite — to je kontinuirani proces. Ali u 2026. imamo bolje alate nego ikad: Hermes s AOT kompilacijom, FlashList v2 s recikliranjem ćelija, Reanimated 4 s CSS animacijama, Zustand i Jotai za state management, te React Native DevTools za precizno profiliranje.
Ključ je pristupiti optimizaciji metodički: najprije izmjerite, identificirajte uska grla alatima za profiliranje, primijenite tehnike iz ovog vodiča, pa ponovno izmjerite.
I da — uvijek testirajte na stvarnim uređajima. Posebno na onim jeftinijim Android uređajima. Emulatori lažu.
I za kraj, jedna stvar koju sam naučio nakon godina rada s React Nativeom: premature optimization is the root of all evil. Pišite čist, razumljiv kod, pa optimizirajte tek kad mjerenja pokažu da trebate. Ali kad trebate — sada znate kako.