πŸ“± Application Architecture

Index

  1. Estructura del Proyecto
  2. Patrones de Arquitectura
  3. Flujo de Datos
  4. Convenciones de CΓ³digo
  5. Capas de la AplicaciΓ³n
  6. Patrones de DiseΓ±o Utilizados
  7. Manejo de Errores
  8. OptimizaciΓ³n y Performance

Project Structure

proyecto/
β”œβ”€β”€ app/                          # Rutas y pantallas (Expo Router)
β”‚   β”œβ”€β”€ (tabs)/                   # NavegaciΓ³n con pestaΓ±as
β”‚   β”‚   β”œβ”€β”€ (home)/              # Tab Home con stack interno
β”‚   β”‚   β”‚   β”œβ”€β”€ _layout.tsx      # Layout del stack
β”‚   β”‚   β”‚   β”œβ”€β”€ index.tsx        # Pantalla principal
β”‚   β”‚   β”‚   └── item-detail.tsx  # Detalle de item
β”‚   β”‚   β”œβ”€β”€ explore/             # Tab Explorar
β”‚   β”‚   β”œβ”€β”€ favorites/           # Tab Favoritos
β”‚   β”‚   β”œβ”€β”€ activity/            # Tab Actividad
β”‚   β”‚   β”œβ”€β”€ messages/            # Tab Mensajes
β”‚   β”‚   β”œβ”€β”€ profile/             # Tab Perfil
β”‚   β”‚   β”œβ”€β”€ settings/            # Tab ConfiguraciΓ³n
β”‚   β”‚   └── _layout.tsx          # ConfiguraciΓ³n de tabs
β”‚   β”œβ”€β”€ _layout.tsx              # Layout raΓ­z
β”‚   β”œβ”€β”€ notifications.tsx        # Modal de notificaciones
β”‚   └── onboarding.tsx           # Pantalla de bienvenida
β”‚
β”œβ”€β”€ components/                   # Componentes reutilizables
β”‚   β”œβ”€β”€ Avatar.tsx               # Componente de avatar
β”‚   β”œβ”€β”€ Button.tsx               # BotΓ³n con variantes
β”‚   β”œβ”€β”€ Card.tsx                 # Tarjeta de contenido
β”‚   β”œβ”€β”€ CategoryChip.tsx         # Chip de categorΓ­a
β”‚   β”œβ”€β”€ EmptyState.tsx           # Empty state
β”‚   β”œβ”€β”€ FilterModal.tsx          # Modal de filtros
β”‚   β”œβ”€β”€ Input.tsx                # Campo de entrada
β”‚   β”œβ”€β”€ SearchBar.tsx            # Barra de bΓΊsqueda
β”‚   β”œβ”€β”€ SettingsRow.tsx          # Fila de configuraciΓ³n
β”‚   β”œβ”€β”€ SkeletonLoader.tsx       # Loader de esqueleto
β”‚   β”œβ”€β”€ StatCard.tsx             # Tarjeta de estadΓ­sticas
β”‚   └── Toast.tsx                # Notificaciones toast
β”‚
β”œβ”€β”€ contexts/                     # Estado global
β”‚   β”œβ”€β”€ AppContext.tsx           # Contexto principal de la app
β”‚   β”œβ”€β”€ ThemeContext.tsx         # Tema (dark/light/system)
β”‚   β”œβ”€β”€ I18nContext.tsx          # InternacionalizaciΓ³n
β”‚   └── ToastContext.tsx         # Sistema de toasts
β”‚
β”œβ”€β”€ constants/                    # Constantes y configuraciΓ³n
β”‚   └── colors.ts                # Colores, espaciado, tipografΓ­a
β”‚
β”œβ”€β”€ mocks/                        # Datos de prueba
β”‚   └── data.ts                  # Datos mock para desarrollo
β”‚
β”œβ”€β”€ types/                        # Definiciones TypeScript
β”‚   └── index.ts                 # Tipos e interfaces globales
β”‚
β”œβ”€β”€ utils/                        # Utilidades y helpers
β”‚   └── helpers.ts               # Funciones auxiliares
β”‚
β”œβ”€β”€ docs/                         # DocumentaciΓ³n
β”‚   β”œβ”€β”€ INDEX.md                 # Índice de documentaciΓ³n
β”‚   β”œβ”€β”€ ARCHITECTURE.md          # Este archivo
β”‚   β”œβ”€β”€ COMPONENTS.md            # GuΓ­a de componentes
β”‚   β”œβ”€β”€ STATE_MANAGEMENT.md      # Manejo de estado
β”‚   β”œβ”€β”€ NAVIGATION.md            # Sistema de navegaciΓ³n
β”‚   β”œβ”€β”€ STYLING.md               # Sistema de diseΓ±o
β”‚   β”œβ”€β”€ ADDING_FEATURES.md       # Agregar funcionalidades
β”‚   β”œβ”€β”€ AUTHENTICATION.md        # Sistema de usuarios
β”‚   β”œβ”€β”€ DATABASE.md              # Base de datos
β”‚   └── REVENUECAT.md            # Compras in-app
β”‚
└── hooks/                        # Custom hooks (crear si se necesita)
    └── useDebounce.ts           # Ejemplo de hook

Architecture Patterns

1. Feature-Based Organization

Organizamos el cΓ³digo por funcionalidad, no por tipo de archivo:

βœ… Correcto (por feature):
app/(tabs)/
β”œβ”€β”€ messages/
β”‚   β”œβ”€β”€ _layout.tsx
β”‚   β”œβ”€β”€ index.tsx          # Lista de chats
β”‚   └── [chatId].tsx       # Individual chat

❌ Incorrecto (por tipo):
screens/
β”œβ”€β”€ MessageList.tsx
β”œβ”€β”€ Chat.tsx
components/
β”œβ”€β”€ MessageItem.tsx

2. SeparaciΓ³n de Responsabilidades

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      UI Layer (Screens)                      β”‚
β”‚  β€’ Renderiza componentes                                     β”‚
β”‚  β€’ Maneja eventos de usuario                                 β”‚
β”‚  β€’ Consume hooks y contextos                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Logic Layer (Contexts/Hooks)              β”‚
β”‚  β€’ Maneja estado global                                      β”‚
β”‚  β€’ Coordina operaciones                                      β”‚
β”‚  β€’ Implementa lΓ³gica de negocio                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Data Layer (React Query)                  β”‚
β”‚  β€’ Fetch de datos                                            β”‚
β”‚  β€’ CachΓ© y sincronizaciΓ³n                                    β”‚
β”‚  β€’ Mutaciones                                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  Persistence Layer (AsyncStorage)            β”‚
β”‚  β€’ Almacenamiento local                                      β”‚
β”‚  β€’ Preferencias de usuario                                   β”‚
β”‚  β€’ Cache offline                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

3. ComposiciΓ³n sobre Herencia

Usamos composiciΓ³n de componentes en lugar de herencia:

// βœ… ComposiciΓ³n
function ProductCard({ product, actions }: ProductCardProps) {
  return (
    <Card>
      <Card.Image source={product.image} />
      <Card.Content>
        <Card.Title>{product.name}</Card.Title>
        <Card.Description>{product.description}</Card.Description>
      </Card.Content>
      <Card.Actions>{actions}</Card.Actions>
    </Card>
  );
}

// ❌ Props excesivas
function ProductCard({ 
  title, 
  description, 
  image, 
  showActions, 
  actionType,
  onAction1,
  onAction2,
  // ... muchas mΓ‘s props
}) { }

Data Flow

Unidirectional Data Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                                                                β”‚
β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚    β”‚ AcciΓ³n  │────▢│ Context  │────▢│ Componentes   β”‚         β”‚
β”‚    β”‚ Usuario β”‚     β”‚ / Query  β”‚     β”‚ (UI Update)   β”‚         β”‚
β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚         β–²                                    β”‚                 β”‚
β”‚         β”‚                                    β”‚                 β”‚
β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β”‚
β”‚                    (Eventos)                                   β”‚
β”‚                                                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Estado y Datos

Tipo de EstadoSoluciΓ³nEjemplo
Estado de UI localuseStateModal abierto/cerrado, input value
Estado de servidorReact QueryLista de productos, datos de usuario
Estado global de appContext + createContextHookUsuario actual, tema, idioma
Persistencia localAsyncStoragePreferencias, cache offline

Ejemplo de Flujo Completo

// 1. Usuario presiona "Agregar a favoritos"
<TouchableOpacity onPress={() => toggleFavorite(item.id)}>

// 2. Context procesa la acciΓ³n
const toggleFavorite = useCallback((itemId: string) => {
  const newFavorites = favorites.includes(itemId)
    ? favorites.filter(id => id !== itemId)
    : [...favorites, itemId];
  
  setFavorites(newFavorites);
  saveMutation.mutate(newFavorites);  // Persiste
}, [favorites, saveMutation]);

// 3. UI se actualiza automΓ‘ticamente
const isFavorite = favorites.includes(item.id);
<Heart fill={isFavorite ? 'red' : 'transparent'} />

Code Conventions

Nombres de Archivos

TipoConvenciΓ³nEjemplo
ComponentesPascalCaseButton.tsx, ProductCard.tsx
HookscamelCase con "use"useDebounce.ts, useAuth.ts
ContextosPascalCase con "Context"AppContext.tsx, ThemeContext.tsx
UtilidadescamelCasehelpers.ts, formatters.ts
TiposPascalCaseindex.ts (dentro de types/)
ConstantescamelCasecolors.ts, config.ts

Estructura de Componentes

// 1. Imports (ordenados: react, react-native, expo, third-party, local)
import React, { useState, useCallback } from 'react';
import { View, Text, StyleSheet, ViewStyle } from 'react-native';
import { useRouter } from 'expo-router';
import { Heart } from 'lucide-react-native';
import { useTheme } from '@/contexts/ThemeContext';
import { Colors, Spacing } from '@/constants/colors';

// 2. Interface de Props
interface MyComponentProps {
  title: string;
  subtitle?: string;
  onPress?: () => void;
  style?: ViewStyle;
  testID?: string;
}

// 3. Componente (function declaration, no arrow)
export function MyComponent({ 
  title, 
  subtitle, 
  onPress, 
  style, 
  testID 
}: MyComponentProps) {
  // 4. Hooks al inicio
  const { colors } = useTheme();
  const router = useRouter();
  const [isLoading, setIsLoading] = useState(false);

  // 5. Callbacks memoizados
  const handlePress = useCallback(() => {
    onPress?.();
  }, [onPress]);

  // 6. Render
  return (
    <View style={[styles.container, { backgroundColor: colors.surface }, style]} testID={testID}>
      <Text style={[styles.title, { color: colors.text }]}>{title}</Text>
      {subtitle && <Text style={[styles.subtitle, { color: colors.textSecondary }]}>{subtitle}</Text>}
    </View>
  );
}

// 7. Estilos al final
const styles = StyleSheet.create({
  container: {
    padding: Spacing.md,
  },
  title: {
    fontSize: 16,
    fontWeight: '600' as const,
  },
  subtitle: {
    fontSize: 14,
    marginTop: Spacing.xs,
  },
});

Convenciones de TypeScript

// βœ… Usar interfaces para props de componentes
interface ButtonProps {
  title: string;
  onPress: () => void;
}

// βœ… Usar type para uniones y alias
type ButtonVariant = 'primary' | 'secondary' | 'outline';
type LoadingState = 'idle' | 'loading' | 'success' | 'error';

// βœ… Explicitar tipos genΓ©ricos en useState
const [items, setItems] = useState<Item[]>([]);
const [selected, setSelected] = useState<string | null>(null);

// βœ… Usar as const para valores literales en estilos
const fontWeight = '600' as const;

// ❌ Evitar any
const data: any = response;  // MAL
const data: unknown = response;  // MEJOR si no conoces el tipo

Application Layers

Capa de PresentaciΓ³n (app/, components/)

Responsabilidades:

  • Renderizar UI
  • Manejar interacciones de usuario
  • Mostrar estados (loading, error, empty)
  • Animaciones y transiciones
// Pantalla tΓ­pica
export default function ProductListScreen() {
  const { products, isLoading, error } = useProducts();
  const { colors } = useTheme();

  if (isLoading) return <SkeletonLoader />;
  if (error) return <ErrorState onRetry={refetch} />;
  if (products.length === 0) return <EmptyState />;

  return (
    <FlatList
      data={products}
      renderItem={({ item }) => <ProductCard product={item} />}
    />
  );
}

Capa de LΓ³gica (contexts/, hooks/)

Responsabilidades:

  • Estado global de la aplicaciΓ³n
  • LΓ³gica de negocio
  • CoordinaciΓ³n entre servicios
  • TransformaciΓ³n de datos
// Contexto tΓ­pico
export const [ProductsProvider, useProducts] = createContextHook(() => {
  const queryClient = useQueryClient();

  const productsQuery = useQuery({
    queryKey: ['products'],
    queryFn: fetchProducts,
  });

  const addProductMutation = useMutation({
    mutationFn: createProduct,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });

  // LΓ³gica de filtrado
  const getFilteredProducts = useCallback((filters: Filters) => {
    return productsQuery.data?.filter(product => {
      if (filters.category && product.category !== filters.category) return false;
      if (filters.minPrice && product.price < filters.minPrice) return false;
      return true;
    }) || [];
  }, [productsQuery.data]);

  return {
    products: productsQuery.data || [],
    isLoading: productsQuery.isLoading,
    addProduct: addProductMutation.mutateAsync,
    getFilteredProducts,
  };
});

Capa de Datos (lib/, utils/)

Responsabilidades:

  • Llamadas a APIs
  • TransformaciΓ³n de respuestas
  • Manejo de errores de red
  • Cache y persistencia
// lib/api/products.ts
const API_URL = process.env.EXPO_PUBLIC_API_URL;

export async function fetchProducts(): Promise<Product[]> {
  const response = await fetch(`${API_URL}/products`);
  
  if (!response.ok) {
    throw new Error(`API Error: ${response.status}`);
  }
  
  const data = await response.json();
  return data.map(transformProduct);
}

function transformProduct(raw: any): Product {
  return {
    id: raw.id,
    name: raw.name,
    price: parseFloat(raw.price),
    image: raw.image_url,
    createdAt: new Date(raw.created_at),
  };
}

Design Patterns Used

Provider Pattern

// Crear provider con createContextHook
export const [ThemeProvider, useTheme] = createContextHook(() => {
  const [mode, setMode] = useState<'light' | 'dark' | 'system'>('system');
  
  const colors = mode === 'dark' ? DarkColors : LightColors;
  const isDark = mode === 'dark';

  return { mode, setMode, colors, isDark };
});

// Usar en _layout.tsx
<QueryClientProvider client={queryClient}>
  <ThemeProvider>
    <AppProvider>
      <RootLayoutNav />
    </AppProvider>
  </ThemeProvider>
</QueryClientProvider>

Compound Components

// Componente compuesto para Cards
function Card({ children, style }: CardProps) {
  return <View style={[styles.card, style]}>{children}</View>;
}

Card.Image = function CardImage({ source }: { source: string }) {
  return <Image source={{ uri: source }} style={styles.image} />;
};

Card.Title = function CardTitle({ children }: { children: string }) {
  return <Text style={styles.title}>{children}</Text>;
};

Card.Actions = function CardActions({ children }: { children: ReactNode }) {
  return <View style={styles.actions}>{children}</View>;
};

// Uso
<Card>
  <Card.Image source={product.image} />
  <Card.Title>{product.name}</Card.Title>
  <Card.Actions>
    <Button title="Comprar" onPress={handleBuy} />
  </Card.Actions>
</Card>

Render Props / Children as Function

// Componente con render props
interface QueryRendererProps<T> {
  query: UseQueryResult<T>;
  children: (data: T) => ReactNode;
  loadingComponent?: ReactNode;
  errorComponent?: ReactNode;
}

function QueryRenderer<T>({ 
  query, 
  children, 
  loadingComponent,
  errorComponent 
}: QueryRendererProps<T>) {
  if (query.isLoading) return loadingComponent || <SkeletonLoader />;
  if (query.error) return errorComponent || <ErrorState />;
  if (!query.data) return null;
  
  return <>{children(query.data)}</>;
}

// Uso
<QueryRenderer query={productsQuery}>
  {(products) => (
    <FlatList data={products} renderItem={...} />
  )}
</QueryRenderer>

Custom Hooks Pattern

// Hook para debounce
function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}

// Hook para formularios
function useForm<T extends Record<string, any>>(initialValues: T) {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({});

  const setValue = useCallback(<K extends keyof T>(key: K, value: T[K]) => {
    setValues(prev => ({ ...prev, [key]: value }));
    setErrors(prev => ({ ...prev, [key]: undefined }));
  }, []);

  const reset = useCallback(() => {
    setValues(initialValues);
    setErrors({});
  }, [initialValues]);

  return { values, errors, setValue, setErrors, reset };
}

Error Handling

Error Boundaries

// components/ErrorBoundary.tsx
import React, { Component, ReactNode } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Button } from './Button';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
  error?: Error;
}

export class ErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false };

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
    // AquΓ­ puedes enviar a un servicio de logging
  }

  handleRetry = () => {
    this.setState({ hasError: false, error: undefined });
  };

  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <View style={styles.container}>
          <Text style={styles.title}>Algo saliΓ³ mal</Text>
          <Text style={styles.message}>{this.state.error?.message}</Text>
          <Button title="Reintentar" onPress={this.handleRetry} />
        </View>
      );
    }

    return this.props.children;
  }
}

Error Handling en Queries

// ConfiguraciΓ³n global de React Query
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 2,
      retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
      staleTime: 1000 * 60 * 5, // 5 minutos
    },
    mutations: {
      onError: (error) => {
        console.error('Mutation error:', error);
        // Mostrar toast global de error
      },
    },
  },
});

// En componentes
const productsQuery = useQuery({
  queryKey: ['products'],
  queryFn: fetchProducts,
  onError: (error) => {
    showToast(error.message, 'error');
  },
});

Optimization and Performance

MemoizaciΓ³n

// Memoizar componentes costosos
const ProductCard = React.memo(function ProductCard({ product }: Props) {
  return (/* ... */);
});

// Memoizar cΓ‘lculos costosos
const filteredProducts = useMemo(() => {
  return products.filter(p => p.category === selectedCategory);
}, [products, selectedCategory]);

// Memoizar callbacks
const handlePress = useCallback(() => {
  onItemPress(item.id);
}, [item.id, onItemPress]);

Listas Optimizadas

// FlatList optimizada
<FlatList
  data={items}
  keyExtractor={(item) => item.id}
  renderItem={renderItem}
  // Optimizaciones
  removeClippedSubviews={true}
  maxToRenderPerBatch={10}
  windowSize={5}
  initialNumToRender={10}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
/>

// Extraer renderItem fuera del componente
const renderItem = useCallback(({ item }: { item: Product }) => (
  <ProductCard product={item} />
), []);

Lazy Loading

// Carga diferida de pantallas pesadas
const HeavyScreen = React.lazy(() => import('./HeavyScreen'));

function App() {
  return (
    <Suspense fallback={<SkeletonLoader />}>
      <HeavyScreen />
    </Suspense>
  );
}

ImΓ‘genes Optimizadas

// Usar expo-image para mejor performance
import { Image } from 'expo-image';

<Image
  source={{ uri: imageUrl }}
  style={styles.image}
  contentFit="cover"
  transition={200}
  placeholder={blurhash}
  cachePolicy="memory-disk"
/>

Diagrama de Dependencias

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                           app/                                   β”‚
β”‚                    (Screens & Navigation)                        β”‚
β”‚    Depende de: components/, contexts/, constants/, types/        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        components/                               β”‚
β”‚                    (UI Components)                               β”‚
β”‚    Depende de: contexts/, constants/, types/, utils/             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         contexts/                                β”‚
β”‚                    (State Management)                            β”‚
β”‚    Depende de: constants/, types/, utils/                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              constants/ + types/ + utils/                        β”‚
β”‚           (ConfiguraciΓ³n, Tipos, Utilidades)                     β”‚
β”‚    Sin dependencias internas                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Reglas:

  • Las capas superiores pueden importar de las inferiores
  • Las capas inferiores NO deben importar de las superiores
  • constants/, types/, utils/ son independientes entre sΓ­