Performance and Lists
Practical ways to keep JS and UI threads responsive - especially around long lists and navigation transitions.
Busca en todas las páginas de la documentación
Practical ways to keep JS and UI threads responsive - especially around long lists and navigation transitions.
Memoize pure row components so parent re-renders do not rebuild every cell.
const Row = memo(function Row({ item }: { item: Item }) {
return <Text>{item.title}</Text>;
});Pass a stable handler into memoized rows (or close over id inside a memoized child).
const onPressItem = useCallback((id: string) => {
router.push(`/items/${id}`);
}, []);Create styles once with StyleSheet.create. Inline objects allocate every render and defeat shallow compares.
// Prefer styles.row over style={{ padding: 12 }} inside renderItem
renderItem={({ item }) => <Row item={item} style={styles.row} />}Fixed-height rows unlock faster scroll-to-index and less measurement work.
const H = 64;
const getItemLayout = (_: unknown, index: number) => ({
length: H,
offset: H * index,
index,
});Schedule parsing or secondary fetches after the transition finishes.
useEffect(() => {
const task = InteractionManager.runAfterInteractions(() => hydrate());
return () => task.cancel();
}, [id]);Mark non-urgent React updates (large filtered lists) so typing stays responsive when using concurrent features.
import { startTransition, useState } from "react";
function onChangeText(text: string) {
setQuery(text);
startTransition(() => setFilter(text));
}Modern RN/Expo defaults to Hermes. Write idiomatic modern JS; avoid assuming JSC-only quirks in new apps.
// Prefer standard ES features supported by Hermes in your RN version
const id = crypto.randomUUID?.() ?? String(Date.now());Mapping hundreds of items into a ScrollView mounts every row at once. Use FlatList / FlashList for long data.
// Avoid for large arrays:
// <ScrollView>{items.map(...)}</ScrollView>
// Prefer:
// <FlatList data={items} renderItem={...} />Always give images width/height or flex constraints so the list does not reflow as bits arrive.
<Image source={{ uri }} style={{ width: 48, height: 48 }} contentFit="cover" />Verbose logs in renderItem jank scroll on device. Strip or gate debug logging.
if (__DEV__ && DEBUG_ROWS) console.log("row", item.id);Use the dev menu perf monitor / React DevTools / native profilers before micro-optimizing.
// Dev Menu -> Show Perf Monitor (FPS / JS frame time)
// Fix the measured bottleneck, not guessed onesDrive high-frequency animations with Reanimated shared values on the UI thread instead of setState per frame.
// Conceptual: const x = useSharedValue(0);
// style useAnimatedStyle(() => ({ transform: [{ translateX: x.value }] }))Multiple updates inside one event handler batch automatically - avoid awaiting between related setters when you need a single paint.
function onSuccess(data: Data) {
setData(data);
setError(null);
setLoading(false);
}Keep stable ids when filtering so row state and images are not remounted incorrectly.
keyExtractor={(item) => item.id}Do not mount every heavy tab eagerly. Lazy-load expensive screens so first paint stays light.
// Expo Router: keep heavy routes as separate files; avoid importing huge modules in root layout
const Editor = React.lazy(() => import("../features/Editor"));Stack versions: React 19.2.3 · React Native 0.86.0 · Expo SDK 57 · Hermes
Revisado por Chris St. John·Última actualización: 18 jul 2026