TechLead
Lección 8 de 8

Optimizacion de Rendimiento

Optimizando renders, listas, imagenes, animaciones y depuracion de rendimiento

Rendimiento en React Native

El rendimiento es crucial para apps moviles. Aqui estan las tecnicas clave para mantener tu app rapida y fluida.

Optimizacion de Listas

import { FlatList, memo } from 'react';

// Componente de item memorizado
const Item = memo(({ titulo, onPress }) => (
  <TouchableOpacity onPress={onPress}>
    <Text>{titulo}</Text>
  </TouchableOpacity>
));

function ListaOptimizada({ datos }) {
  // Memoizar renderItem
  const renderItem = useCallback(({ item }) => (
    <Item
      titulo={item.titulo}
      onPress={() => console.log(item.id)}
    />
  ), []);

  // Memoizar keyExtractor
  const keyExtractor = useCallback((item) => item.id.toString(), []);

  return (
    <FlatList
      data={datos}
      renderItem={renderItem}
      keyExtractor={keyExtractor}
      // Optimizaciones
      removeClippedSubviews={true}
      maxToRenderPerBatch={10}
      windowSize={5}
      initialNumToRender={10}
      getItemLayout={(data, index) => ({
        length: 80, // altura fija del item
        offset: 80 * index,
        index,
      })}
    />
  );
}

Memorizacion de Componentes

import { memo, useMemo, useCallback } from 'react';

// memo para componentes
const ComponenteCostoso = memo(function ComponenteCostoso({ datos }) {
  // Solo se re-renderiza si datos cambia
  return <View>{/* contenido */}</View>
});

// useMemo para calculos costosos
function ComponenteConCalculo({ items }) {
  const itemsProcesados = useMemo(() => {
    return items.filter(i => i.activo).sort((a, b) => a.orden - b.orden);
  }, [items]);

  return <FlatList data={itemsProcesados} />;
}

// useCallback para funciones
function Padre() {
  const [cuenta, setCuenta] = useState(0);

  const handlePress = useCallback(() => {
    setCuenta(c => c + 1);
  }, []);

  return <Hijo onPress={handlePress} />;
}

Optimizacion de Imagenes

import { Image } from 'react-native';
import FastImage from 'react-native-fast-image';

// Imagen basica con cache
<Image
  source={{ uri: 'https://ejemplo.com/imagen.jpg' }}
  style={{ width: 200, height: 200 }}
  resizeMode="cover"
/>

// FastImage (mejor rendimiento y cache)
<FastImage
  source={{
    uri: 'https://ejemplo.com/imagen.jpg',
    priority: FastImage.priority.high,
    cache: FastImage.cacheControl.immutable,
  }}
  style={{ width: 200, height: 200 }}
  resizeMode={FastImage.resizeMode.cover}
/>

// Consejos:
// - Usa imagenes del tamano correcto
// - Comprime imagenes antes de subirlas
// - Usa formato WebP cuando sea posible
// - Implementa placeholder mientras carga

Animaciones Optimizadas

import { Animated, useRef } from 'react';
import Reanimated, {
  useSharedValue,
  withSpring,
  useAnimatedStyle
} from 'react-native-reanimated';

// Con Animated (JS thread)
function AnimacionBasica() {
  const fadeAnim = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 500,
      useNativeDriver: true, // Importante!
    }).start();
  }, []);

  return (
    <Animated.View style={{ opacity: fadeAnim }}>
      <Text>Fade In</Text>
    </Animated.View>
  );
}

// Con Reanimated (UI thread - mejor rendimiento)
function AnimacionReanimated() {
  const offset = useSharedValue(0);

  const animatedStyles = useAnimatedStyle(() => ({
    transform: [{ translateX: withSpring(offset.value) }],
  }));

  return (
    <Reanimated.View style={animatedStyles}>
      <Text>Animado</Text>
    </Reanimated.View>
  );
}

Evitar Re-renders Innecesarios

// MAL - crea nuevo objeto en cada render
<View style={{ flex: 1, padding: 10 }}>

// BIEN - estilos fuera del componente
const styles = StyleSheet.create({
  contenedor: { flex: 1, padding: 10 }
});
<View style={styles.contenedor}>

// MAL - funcion inline crea nueva referencia
<Button onPress={() => handlePress(id)} />

// BIEN - useCallback
const onPress = useCallback(() => handlePress(id), [id]);
<Button onPress={onPress} />

// MAL - objeto nuevo cada render
<Componente datos={{ nombre: 'Ana' }} />

// BIEN - memoizar
const datos = useMemo(() => ({ nombre: 'Ana' }), []);

Herramientas de Depuracion

// React DevTools
// Instala la extension de navegador o standalone

// Flipper (depurador de escritorio)
// https://fbflipper.com

// Performance Monitor (en desarrollo)
// Agitar dispositivo -> "Show Perf Monitor"

// Console para medir tiempo
console.time('operacion');
// ... codigo
console.timeEnd('operacion');

// React.Profiler
<Profiler id="MiComponente" onRender={onRenderCallback}>
  <MiComponente />
</Profiler>

Lista de Verificacion de Rendimiento

  • Usa FlatList en lugar de ScrollView para listas
  • Memoriza componentes pesados con memo
  • Siempre usa useNativeDriver: true en animaciones
  • Optimiza imagenes (tamano, formato, cache)
  • Evita crear funciones/objetos inline en render
  • Usa React DevTools Profiler para identificar problemas
  • Mantener 60 FPS - monitorear con Performance Monitor