Ejemplos de código
Patrones chicos que puedes llevar a tu app. Arregla los imports a tus alias (me gusta @/), mira versiones contra tu SDK y si toca nativo, pruébalo en iOS y Android de verdad.
Cabecera que no se come el notch
react-native-safe-area-context te salva de pintar bajo el notch, el Dynamic Island o la barra de inicio.
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { View, Text } from 'react-native';
export function ScreenHeader({ title }: { title: string }) {
const insets = useSafeAreaInsets();
return (
<View style={{ paddingTop: insets.top, paddingHorizontal: 16 }}>
<Text style={{ fontSize: 18, fontWeight: '600' }}>{title}</Text>
</View>
);
}FlatList con carga y “no hay nada”
Spinner al principio, luego filas o un vacío sencillo que el usuario entiende.
import { FlatList, Text, View, ActivityIndicator } from 'react-native';
type Item = { id: string; title: string };
export function ItemList({
data,
loading,
}: {
data: Item[];
loading: boolean;
}) {
if (loading && data.length === 0) {
return (
<View style={{ flex: 1, justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
if (!loading && data.length === 0) {
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 24 }}>
<Text style={{ textAlign: 'center' }}>Nothing here yet.</Text>
</View>
);
}
return (
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 16 }}>
<Text>{item.title}</Text>
</View>
)}
/>
);
}Búsqueda que espera a que dejes de teclear
Debounce corto para no martillar la API en cada tecla; limpieza al desmontar incluida.
import { useEffect, useState } from 'react';
import { TextInput } from 'react-native';
export function SearchField({ onQuery }: { onQuery: (q: string) => void }) {
const [text, setText] = useState('');
useEffect(() => {
const id = setTimeout(() => onQuery(text.trim()), 350);
return () => clearTimeout(id);
}, [text, onQuery]);
return (
<TextInput
value={text}
onChangeText={setText}
placeholder="Search"
style={{ borderWidth: 1, borderRadius: 8, padding: 12 }}
/>
);
}Tirar para refrescar en un ScrollView
RefreshControl con trabajo async y siempre quita el spinner, aunque la petición falle.
import { useCallback, useState, type ReactNode } from 'react';
import { ScrollView, RefreshControl } from 'react-native';
export function RefreshableScroll({ children }: { children: ReactNode }) {
const [refreshing, setRefreshing] = useState(false);
const onRefresh = useCallback(async () => {
setRefreshing(true);
try {
await new Promise((r) => setTimeout(r, 800));
} finally {
setRefreshing(false);
}
}, []);
return (
<ScrollView
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
>
{children}
</ScrollView>
);
}¿Quieres el árbol entero o la referencia del helper enorme? Eso está en estructura y utilidades.