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:

  1. 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

}

  1. 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

  1. Explorar componentes: Crear tu primer componente personalizado

  2. Modificar App.tsx: Cambiar la UI principal

  3. Agregar dependencias: Instalar librerías útiles

  4. Configurar navegación: Implementar múltiples pantallas

  5. 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

Entradas más populares de este blog

Guía Paso a Paso para Entender React Native (antes del Tutorial)

Tutorial: Aplicación React Native para Agregar Tareas - Minimalista

5. Vista Rapida: Estructura Projecto Base