17. Estructura de una aplicación de React Native, una vista mas profunda
Tutorial: Estructura de una Aplicación React Native - Una Vista Más Profunda
En este tutorial exploraremos en detalle la estructura de un proyecto React Native, analizando cada archivo y carpeta para entender completamente cómo funciona.
🎯 Objetivo del Tutorial
Comprender la estructura interna de un proyecto React Native, el propósito de cada archivo y cómo interactúan entre sí.
📁 Estructura Completa del Proyecto
text
MiPrimeraApp/
│
├── __tests__/ # Pruebas unitarias
├── .bundle/ # Configuración del bundle
├── .vscode/ # Configuración de VS Code
├── android/ # Proyecto nativo Android
├── ios/ # Proyecto nativo iOS
├── node_modules/ # Dependencias JavaScript
├── vendor/ # Optimización para iOS
│
├── .eslintrc.js # Configuración ESLint
├── .gitignore # Archivos ignorados por Git
├── .node-version # Versión de Node.js
├── .prettierrc.js # Configuración Prettier
├── .watchmanconfig # Configuración Watchman
├── app.json # Configuración de la app
├── App.tsx # Componente principal
├── babel.config.js # Configuración Babel
├── Gemfile # Dependencias Ruby (iOS)
├── Gemfile.lock # Lock de dependencias Ruby
├── index.js # Punto de entrada
├── metro.config.js # Configuración Metro Bundler
├── package.json # Dependencias y scripts
├── tsconfig.json # Configuración TypeScript
└── yarn.lock # Lock de dependencias Yarn
🔍 Análisis Detallado por Carpeta/Archivo
1. Carpeta: __tests__/
Propósito: Contiene pruebas unitarias automatizadas.
javascript
// Ejemplo: App-test.js
import 'react-native';
import React from 'react';
import App from '../App';
// Importar renderer para crear snapshot
import renderer from 'react-test-renderer';
it('renders correctly', () => {
// Crear snapshot del componente App
const tree = renderer.create(<App />).toJSON();
// Comparar con snapshot guardado
expect(tree).toMatchSnapshot();
});
Uso:
bash
# Ejecutar pruebas
npm test
# Ejecutar pruebas en modo watch
npm test -- --watch
# Generar nuevo snapshot
npm test -- --updateSnapshot
2. Carpeta: .bundle/
Propósito: Configuración del proceso de bundling para producción.
json
// Contenido típico
{
"bundleType": "BASIC",
"entryFile": "index.js",
"bundleOutput": "android/app/src/main/assets/index.android.bundle",
"sourcemapOutput": "android/app/src/main/assets/index.android.map",
"platform": "android"
}
Importancia: Define cómo se empaqueta el código JavaScript para la aplicación nativa.
3. Carpeta: .vscode/
Propósito: Configuración específica del proyecto para VS Code.
json
// settings.json - Configuración del editor
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.autoSave": "onFocusChange",
"typescript.preferences.importModuleSpecifier": "relative"
}
// launch.json - Configuración de debugging
{
"configurations": [
{
"name": "Debug Android",
"request": "launch",
"type": "reactnative",
"platform": "android"
}
]
}
4. Carpeta: android/
Estructura del proyecto nativo Android:
text
android/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── miprimeraapp/
│ │ │ │ └── MainActivity.kt // Actividad principal
│ │ │ │ └── MainApplication.kt // Aplicación principal
│ │ │ ├── res/ // Recursos (imágenes, strings)
│ │ │ └── AndroidManifest.xml // Configuración de la app
│ │ └── build.gradle // Dependencias del módulo app
│ └── build.gradle // Dependencias del proyecto
├── gradle/
├── gradlew // Wrapper de Gradle
└── settings.gradle // Configuración del proyecto
Archivos clave:
app/build.gradle - Configuración de build:
groovy
android {
compileSdkVersion 33
defaultConfig {
applicationId "com.miprimeraapp" // ID único de la app
minSdkVersion 21
targetSdkVersion 33
versionCode 1 // Versión interna
versionName "1.0" // Versión visible
}
}
dependencies {
implementation "com.facebook.react:react-native:+" // React Native
implementation project(':react-native-vector-icons') // Dependencias nativas
}
AndroidManifest.xml - Permisos y configuración:
xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MainApplication"
android:label="@string/app_name">
<activity
android:name=".MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
5. Carpeta: ios/
Estructura del proyecto nativo iOS:
text
ios/
├── MiPrimeraApp/
│ ├── AppDelegate.mm // Punto de entrada iOS
│ ├── Main.m // Método main
│ └── Info.plist // Configuración de la app
├── MiPrimeraApp.xcodeproj/ // Proyecto Xcode
├── MiPrimeraApp.xcworkspace/ // Workspace Xcode
└── Podfile // Dependencias CocoaPods
Archivo clave: Podfile - Dependencias iOS:
ruby
platform :ios, '12.0'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
target 'MiPrimeraApp' do
config = use_native_modules!
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => flags[:hermes_enabled]
)
end
6. Carpeta: node_modules/
Propósito: Contiene todas las dependencias JavaScript instaladas.
bash
# Estructura de dependencias clave
node_modules/
├── react/ # Biblioteca React core
├── react-native/ # Biblioteca React Native
├── @types/ # Definiciones TypeScript
├── metro-react-native-babel-preset/ # Preset Babel para React Native
└── [otras 1000+ dependencias] # Dependencias transitivas
No debe versionarse:
gitignore
# En .gitignore
node_modules/
7. Carpeta: vendor/
Propósito: Optimizaciones para build de iOS (Ruby scripts).
ruby
# Scripts para optimizar la compilación
# Normalmente contiene:
# - Scripts de post-instalación
# - Optimizaciones de performance
# - Fixes para problemas específicos de iOS
📄 Archivos de Configuración en Raíz
1. .eslintrc.js - Linter de código
javascript
module.exports = {
root: true,
extends: '@react-native-community',
rules: {
'prettier/prettier': 0, // Desactiva conflictos con Prettier
'react-native/no-inline-styles': 0, // Permite estilos inline
'no-unused-vars': 'warn', // Advertencia para variables no usadas
},
};
2. .gitignore - Archivos ignorados por Git
gitignore
# Dependencias
node_modules/
*.log
npm-debug.log*
# Producción
android/app/build/
ios/build/
# Variables de entorno
.env
3. .node-version - Control de versión
text
18.16.0
Uso: Herramientas como nvm usan este archivo para cambiar automáticamente a la versión correcta.
4. .prettierrc.js - Formateo de código
javascript
module.exports = {
bracketSpacing: false,
singleQuote: true,
trailingComma: 'all',
};
5. .watchmanconfig - Sistema de watch de archivos
json
{
"ignore_dirs": ["node_modules", "android/build", "ios/build"]
}
Propósito: Watchman observa cambios en archivos para recargar automáticamente.
6. app.json - Metadatos de la aplicación
json
{
"name": "MiPrimeraApp",
"displayName": "MiPrimeraApp",
// Configuración específica por plataforma
"android": {
"package": "com.miprimeraapp"
},
"ios": {
"bundleIdentifier": "com.miprimeraapp"
}
}
7. App.tsx / App.jsx - Componente principal
typescript
import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
// Componente funcional principal
const App: React.FC = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>¡Hola React Native!</Text>
</View>
);
};
// Estilos con StyleSheet
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
text: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default App;
8. babel.config.js - Transformador de código
javascript
module.exports = {
presets: [
'module:metro-react-native-babel-preset', // Preset oficial React Native
],
plugins: [
'@babel/plugin-transform-runtime', // Transformaciones adicionales
'react-native-reanimated/plugin', // Plugin para animaciones
],
};
¿Qué hace Babel?
javascript
// Convierte JSX y ES6+ a código compatible
// De:
const Component = () => <View>Hola</View>;
// A:
const Component = () => React.createElement(View, null, 'Hola');
9. Gemfile y Gemfile.lock - Dependencias Ruby
ruby
# Gemfile
source 'https://rubygems.org'
gem 'cocoapods', '~> 1.12' # Gestor de dependencias iOS
10. index.js - Punto de entrada principal
javascript
// Importar registro de aplicación
import {AppRegistry} from 'react-native';
// Importar componente principal
import App from './App';
// Importar nombre de la app
import {name as appName} from './app.json';
// Registrar componente principal
AppRegistry.registerComponent(appName, () => App);
Flujo:
text
index.js → AppRegistry → App.tsx → Renderizado nativo
11. metro.config.js - Bundler de React Native
javascript
const {getDefaultConfig} = require('metro-config');
module.exports = (async () => {
const {
resolver: {sourceExts, assetExts},
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true, // Optimización de performance
},
}),
},
resolver: {
assetExts: assetExts.filter(ext => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
},
};
})();
¿Qué hace Metro?
Empaqueta código JavaScript
Transforma assets (imágenes, fuentes)
Hace hot reloading
Genera source maps para debugging
12. package.json - Corazón del proyecto
json
{
"name": "MiPrimeraApp",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint .",
"build:android": "cd android && ./gradlew assembleRelease",
"build:ios": "cd ios && xcodebuild -workspace MiPrimeraApp.xcworkspace -scheme MiPrimeraApp -configuration Release"
},
"dependencies": {
"react": "18.2.0",
"react-native": "0.72.0",
"react-native-vector-icons": "^9.2.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native-community/eslint-config": "^3.2.0",
"@types/react": "^18.0.24",
"@types/react-native": "^0.70.0",
"@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.2.1",
"eslint": "^8.19.0",
"jest": "^29.2.1",
"prettier": "^2.4.1",
"react-test-renderer": "18.2.0",
"typescript": "^4.8.4"
},
"jest": {
"preset": "react-native"
},
"engines": {
"node": ">=18.0.0"
}
}
13. tsconfig.json - Configuración TypeScript
json
{
"extends": "@tsconfig/react-native/tsconfig.json",
"compilerOptions": {
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "react-native"
},
"include": [
"src/**/*",
"**/*.test.ts",
"**/*.test.tsx"
],
"exclude": [
"node_modules",
"babel.config.js",
"metro.config.js",
"jest.config.js"
]
}
14. yarn.lock / package-lock.json - Lock de dependencias
Propósito: Congela versiones exactas de dependencias para reproducibilidad.
🔄 Flujo de Ejecución Completado
text
1. Usuario abre la app
↓
2. Sistema nativo (Android/iOS) inicia
↓
3. MainActivity/MainApplication carga
↓
4. React Native inicializa JavaScript Core/Hermes
↓
5. Ejecuta index.js desde assets
↓
6. AppRegistry.registerComponent() registra App
↓
7. App.tsx se renderiza
↓
8. JSX se convierte a componentes nativos
↓
9. Bridge comunica JS → Nativo
↓
10. UI nativa se muestra al usuario
🛠️ Buenas Prácticas de Estructura
Organización recomendada para crecimiento:
text
src/
├── components/ # Componentes reutilizables
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.styles.ts
│ │ └── Button.test.tsx
│ └── Card/
├── screens/ # Pantallas completas
│ ├── Home/
│ └── Profile/
├── navigation/ # Configuración de navegación
│ ├── AppNavigator.tsx
│ └── routes.ts
├── services/ # Lógica de negocio, APIs
│ ├── api/
│ └── storage/
├── hooks/ # Custom hooks
├── utils/ # Funciones helper
├── constants/ # Constantes, temas, colores
├── assets/ # Imágenes, fuentes, íconos
├── types/ # Definiciones TypeScript
└── context/ # Context API providers
Crear estructura inicial:
bash
mkdir -p src/{components,screens,navigation,services,hooks,utils,constants,assets,types,context}
✅ Checklist de Entendimiento
Comprendo la función de __tests__/ para pruebas unitarias
Entiendo que android/ y ios/ contienen proyectos nativos
Sé que node_modules/ contiene todas las dependencias
Reconozco que App.tsx es el componente principal
Comprendo que index.js es el punto de entrada
Entiendo cómo metro.config.js empaqueta el código
Sé que package.json define dependencias y scripts
Reconozco el propósito de cada archivo de configuración
🎯 Conclusión
Has explorado en profundidad la estructura de un proyecto React Native:
✅ Carpetas nativas: android/, ios/ - Código específico de plataforma
✅ Configuración: Archivos de setup para herramientas
✅ Código fuente: App.tsx, index.js - Lógica de la aplicación
✅ Dependencias: package.json, node_modules/ - Bibliotecas externas
✅ Herramientas: Configuración para linting, testing y formateo
📚 Próximos Pasos
Explorar componentes: Crear tu primer componente personalizado
Modificar App.tsx: Cambiar la UI principal
Agregar dependencias: Instalar librerías útiles
Configurar navegación: Implementar múltiples pantallas
Conectar APIs: Consumir datos de servicios externos
¡Excelente! Ahora tienes un entendimiento profundo de cómo está estructurado un proyecto React Native. Esto te permitirá trabajar de manera más efectiva y comprender mejor cómo funciona cada parte de tu aplicación
Comentarios
Publicar un comentario