La inyección de dependencias en móvil significa pasar dependencias - clientes de API, almacenamiento, analítica - en lugar de importar singletons. Los equipos de React Native obtienen servicios probables con funciones factory, React context, y límites de módulos - no Dagger, Spring, o un service locator global.
// src/shared/api/createApiClient.tsexport type ApiClient = { get<T>(path: string): Promise<T>;};export function createApiClient(baseUrl: string): ApiClient { return { async get<T>(path: string) { const response = await fetch(`${baseUrl}${path}`); if (!response.ok) throw new Error(`GET ${path} falló`); return response.json() as Promise<T>; }, };}
// src/features/orders/api/ordersApi.tsimport type { Order } from "@/entities/order";import type { ApiClient } from "@/shared/api/createApiClient";export type OrdersApi = { list(): Promise<Order[]>;};export function createOrdersApi(client: ApiClient): OrdersApi { return { list: () => client.get<Order[]>("/orders"), };}
// ❌ Global oculto - difícil de probar, difícil de intercambiar por varianteexport const ordersApi = createOrdersApi(createApiClient(getAppConfig().apiBaseUrl));
// ✅ Cableado explícito en raíz de composiciónconst client = createApiClient(getAppConfig().apiBaseUrl);const ordersApi = createOrdersApi(client);
Context dios con 40 servicios - Todo se re-renderiza en cualquier cambio de override. Solución: Divide por feature o usa objetos de servicios estables con useMemo.
Crear nuevas instancias de servicios en cada render - Rompe deps de useCallback y refetches sin fin. Solución: Memoiza servicios en provider con useMemo(() => create..., [baseUrl]).
Importar adaptadores concretos en use cases - Invierte arquitectura limpia. Solución: Los use cases aceptan interfaces de puertos; solo la raíz de composición importa restAuthGateway.
Sorpresas de hoisting de jest.mock - La ruta de mock debe coincidir exactamente con la importación. Solución: Prefiere prop overrides en provider sobre mocks de módulos cuando sea posible.
URLs de servicios Expo Go vs dev build - Ramas __DEV__ ocultas en factories. Solución:getAppConfig().apiBaseUrl desde app.config.ts extra por variante.
Analítica singleton en cada archivo - Spam de eventos no probables. Solución:AnalyticsPort inyectado; implementación noop en tests.
tsyringe para tres servicios - Dolor de metadata de reflection en Metro. Solución: Factories simples hasta que tengas 20+ tipos cableados.
¿Necesitan las apps de React Native un marco de trabajo de inyección de dependencias?
No. Las funciones factory más React context cubren el 95% de necesidades móviles. Los contenedores añaden indirección sin resolver problemas específicos de React Native (bundling Metro, Fast Refresh).
¿Qué es la raíz de composición?
El único lugar donde implementaciones concretas se vinculan a abstracciones - típicamente app/_layout.tsx o AppServicesProvider. Ningún otro archivo debe llamar a createRestAuthGateway con URLs de producción.
¿Cómo pruebo hooks que usan clientes de API?
Envuelve con AppServicesProvider overrides={{ ordersApi: mock }}. Alternativamente pasa la API como argumento hook solo en tests.
¿Context vs Zustand para servicios?
Context cablead instancias de servicios inmutables (API, almacenamiento). Zustand mantiene estado de app mutable (carrito, sesión). No almacenes clientes fetch en Zustand a menos que tengan tokens de sesión que cambien.
¿Puedo usar loaders de expo-router para inyección de dependencias?
Expo Router no tiene loaders RSC. Cablead servicios en providers de layout raíz - no por ruta a menos que las deps de ruta estén realmente aisladas.
¿Cómo obtienen variantes de entorno diferentes APIs?
getAppConfig() lee extra desde app.config.ts - la raíz de composición pasa el baseUrl correcto cuando crea clientes. Sin if en código de feature.
¿Qué pasa con @tanstack/react-query?
Inyecta un QueryClient via context (built-in QueryClientProvider). Los adaptadores de repositorio alimentan funciones de query - queryFn: () => ordersApi.list().
¿Es jest.mock malo?
Aceptable para tests de integración. Prefiere provider overrides para tests unitarios - más claro y tipado.
¿Cómo evito importaciones circulares con inyección de dependencias?
Los puertos viven en domain/ports.ts. Los adaptadores importan puertos + entidades. La raíz de composición importa adaptadores. Las features importan puertos a través de context - nunca adaptadores del dominio.
¿Debe inyectarse SecureStore?
Sí - envuelve en puerto TokenStorage. Los tests usan almacenamiento en memoria; producción usa createSecureTokenStorage().
¿Fast Refresh con providers de context?
Cambiar la implementación del provider recarga en caliente bien. Cambiar la forma del context puede necesitar recarga completa - mantén el tipo AppServices estable.
¿Múltiples apps en monorepo - inyección de dependencias compartida?
packages/services exporta factories; cada composición raíz apps/* pasa config específica de app. No compartas un singleton entre apps.
¿Cuándo tiene sentido una librería de inyección de dependencias?
Raramente en React Native. Considéralo cuando portes patrones de un equipo backend grande o cuando tengas 30+ interfaces cableadas con tests de contrato automatizados - aún así evalúa primero el costo de Metro.
¿Cómo se relaciona la inyección de dependencias con la arquitectura limpia?
Los puertos son interfaces; los adaptadores son implementaciones; la raíz de composición los vincula. Los use cases reciben deps como argumentos de función - la forma más pura de inyección.