React Native nos permite crear aplicaciones reales nativas para iOS y Android basado en React JS. Utilizando ReactJS, en lugar de obtener una aplicación web híbrida, obtenemos una aplicación real nativa.
Una aplicación híbrida es aquella que ejecuta código HTML, CSS y Javascript dentro de un componente de la aplicación móvil destinado a cargar páginas web (una especie de iframe). Una aplicación nativa es la que está programada utilizando el lenguaje nativo de la plataforma.
El código que programemos usando React Native será traducido al lenguaje nativo de la plataforma, mientras que cuando desarrollamos una aplicación híbrida, ejecutamos código HTML, CSS y JavaScript dentro de esta especie de iframe.
Para programar una aplicación nativa para Android, necesitaríamos conocer el lenguaje Kotlin, mientras que si la aplicación es para IOS, necesitaríamos saber Swift. Sin embargo, con React Native, podremos realizar una aplicación nativa para ambas plataformas conociendo una sola tecnología.
Las aplicaciones nativas, respecto de las aplicaciones híbridas, tienen:
- Mejor rendimiento
- Menor consumo de memoria.
- Mayor velocidad.
Crear una aplicación en React Native con Expo
Documentación de la instalación.
1. Instalamos Expo en el sistema operativo:
npm install -g expo-cli
2. Creamos nuestro proyecto Expo. Si estamos usando windows, es recomendable que ejecutemos este comando desde la cmd, no desde powershell ni git bash.
expo init AwesomeProject
Si al ejecutar el comando anterior obtuviesemos este error:
expo is not recognized as an internal or external command
Debemos añadir npm a nuestro conjunto de variables de entorno. En windows, sería: botón de inicio → Editar variables de entorno del sistema → Variables de entorno … → Path → %USERPROFILE%\AppData\Roaming\npm
3. Ejecutamos el proyecto que hemos creado
npm start
4. Instalamos la aplicación Expo en el teléfono móvil: Expo
5. Para desplegar escanemos el código qr generado. Para evitar problemas…
- En el teléfono móvil Android, iremos a ajustes → Aplicaciones → Expo → Mostrar sobre otras apps.
- Ejecutaremos bajo la opción Tunnel connection. Debemos esperar a que aparezca el mensaje Tunnel Ready para escanear el código de barras.
Ejecutando la aplicación en el emulador
1. Debemos tener instalado Android Studio.
2. Si estamos usando windows, tendremos que añadir las siguientes variables de entorno a nivel de usuario:
ANDROID_HOME → C:\Users(name)\AppData\Local\Android\Sdk
Añadir al Path → C:\Users(name)\AppData\Local\Android\Sdk\platform-tools
2. Creamos y arrancamos un nuevo Device Manager:

Componentes básicos de React Native
Equivalente a los div de HTML. Sirve para agrupar componentes.
import { View } from 'react-native';
<View><View>
import { Text } from 'react-native';
<Text>El texto<Text>
<TextInput onChangeText={text => setStateValue(text)}/>
import { Image } from 'react-native';
import imagen from 'ruta-imagen/imagen.png';
<Image source={imagen} />
Cargar un combo desplegable:
<Picker style={styles.container}
selectedValue={microCicloActivo}
onValueChange={valor => setValor(valor)}>
<Picker.Item key=0 value=0 label="titulo 0"/>
<Picker.Item key=1 value=1 label="titulo 1"/>
<Picker.Item key=2 value=2 label="titulo 2"/>
</Picker>
<Button title="Cerrar" onPress={ btnPulsado }/>
El componete Button no se puede estilizar (sólo en Android). En su lugar, usaremos TouchableOpacity.
Listado de elementos:
<FlatList
data={pacients}
renderItem={({ item }) => <Text>{item}</Text>}
keyExtractor={item => item}
/>
styled-components en react-native
Es posible utilizar Styled Components en React native sin ningún tipo de limitación. Sin embargo, para maquetar los componentes en React Native, hemos de tener en cuenta lo siguiente:
- El valor por defecto el estilo display es flex. Los posibles valores de este estilo son: flex y none.
- El valor por defecto del estilo flex-direction es column, al contrario de lo que ocurre en una aplicación web, es es row.
- La unidad de medida que usaremos será px. Ni %, ni em, ni rem.
import React from 'react';
import { View } from 'react-native';
import styled from 'styled-components/native';
export const VerticalLayout = styled.View`
flex-direction: column;
`;
Al usar styled-components en React native no puedes mezclarlos con estilos nativos en línea. El siguiente ejemplo sería incorrecto:
<MyStyledComponent style={{marginTop:'6px'}} />
Media queries
import { Dimensions } from 'react-native';
const { width, height } = Dimensions.get('window');
const isMobile = width <= 700;
export const ColLeft = styled.View`
flex-direction:${isMobile ? 'column' : 'row'};
`;
Cargar tipografías
import React from 'react';
import {useFonts} from 'expo-font';
import AppLoading from 'expo-app-loading';
import QuicksandRegular from './assets/fonts/Quicksand-Light.ttf';
import {View, Text} from 'react-native';
export default () => {
const [fontsLoaded] = useFonts({'QuicksandRegular': 'QuicksandRegular'});
return !fontsLoaded ? <AppLoading/> :
<View
style={{justifyContent: 'center', alignItems: 'center', flex: 1}}
>
<Text
style={{fontFamily: 'QuicksandRegular', fontSize: 30}}
>
Pablo Monteserín
</Text>
</View>
}
Navigation
Stack Navigator
La stack navigation es similar a la que realizamos en nuestro navegador web mediante los botones de avanzar y retroceder página.
Imagina que tu aplicación móvil tiene tres páginas: A, B y C. Empiezas tu navegación en la página A. Pulsas un botón que te lleva a la página B. El stack queda así: B, A y C. Además, aparece el botón en pantalla de ir a la página anterior, en este caso, la página A. Si lo pulso, el stack quedaría así: A, B y C. Si pulso en ir a la página C, el stack quedaría así: C, A, B. Nuevamente, si pulso en ir a la página anterior, iríamos a la página A y la página C quedaría en la última posición. El stack quedaría así: A, B y C.
El stack es el contenedor donde se precargan las pantallas.
npm i react-navigation
npm i @react-navigation/native
npm i react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view
npm i @react-navigation/stack
./App.js
import 'react-native-gesture-handler';
import React from 'react';
import Navigation from './src/navigation';
export default () => <Navigation />
./src/navigation/index.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import Main from './stacks/index'
export default () => <NavigationContainer><Main/></NavigationContainer>
./src/navigation/stacks/index.js
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import Home from '../../screens/home';
import Contact from '../../screens/contact';
const Stack = createStackNavigator();
export default () => (
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Contact" component={Contact} />
</Stack.Navigator>
);
./src/screens/home/index.js
import React from 'react';
import { View, Text, Button } from "react-native";
export default ({ navigation, route }) =>
<View>
<Text>screen {route.name}</Text>
<Button title="Change Screen" onPress={() =>
navigation.navigate('Contact')
}></Button>
</View>
Descargar ejemplo de stack navigation
Con parámetros
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
Bottom Navigation
Aunque es posible crear un menú Bottom Navigation, sin integrarlo dentro de un Stack Navigation, te recomiendo integrarlo siempre dentro de un Stack Navigation, ya que esto te dará más libertad para hacer desaparecer dicho menú cuando naveges a ciertas pantallas.
npm i react-navigation
npm i @react-navigation/native
npm i @react-navigation/bottom-tabs
A continuación vamos a cambiar, dentro del stack de navegación, el componente Home por el componente BottomNavigation. Esto hará que inicialmente se vea la pantalla Home, por ser el primer elemento del bottom navigation.
./src/navigation/stacks/index.js
export default () => (
<Stack.Navigator>
<Stack.Screen name="BottomNavigation" component={BottomNavigation} />
<Stack.Screen name="Contact" component={Contact} />
</Stack.Navigator>
);
Si eliminásemos el elemento Contact del bottom navigation, al navegar a la pantalla de Contact no se verá el Bottom Navigation.
./src/navigation/bottom/index.js
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import Home from '../../screens/home';
import Contact from '../../screens/home';
const Tab = createBottomTabNavigator();
export default () => (
<Tab.Navigator>
<Tab.Screen name="Home" component={Home}
options={{
title: 'Inicio',
tabBarIcon: () => <Image source={img} />
}}
/>
<Tab.Screen name="Contact" component={Contact} />
</Tab.Navigator>
);
Botón de volver para atrás
import React, {useEffect} from 'react';
import { Text, TouchableOpacity, BackHandler } from 'react-native';
export default ({navigation}) => {
const HARDWARE_BACK_PRESS = React.useRef('hardwareBackPress');
const handleBackButtonClick = _ => {
navigation.goBack();
return true;
}
useEffect(() => {
BackHandler.addEventListener(HARDWARE_BACK_PRESS, handleBackButtonClick);
return () => {
BackHandler.removeEventListener(HARDWARE_BACK_PRESS, handleBackButtonClick);
};
}, []);
return (
<TouchableOpacity onPress={handleBackButtonClick}>
<Text>Pulsame</Text>
</TouchableOpacity>
)
}
Dejar un espacio en la parte superior de la pantalla para la status bar
El siguiente código funciona en IOS y Android.
import React from 'react';
import {Platform, StatusBar} from 'react-native';
import styled from 'styled-components/native';
export default () => <MySafeAreaView></MySafeAreaView>
export const MySafeAreaView = styled.SafeAreaView`
padding-top: ${Platform.OS === 'android' ? StatusBar.currentHeight : 0};
`;
El componente SafeArea deja una separación para la status bar, pero sólo funciona en IOS. Para que funcione en Android, creado el anterior styled-component.
Módulos útiles
React native maps
Integra un mapa de google maps en nuestra aplicación.
https://www.npmjs.com/package/react-native-maps
Date Time Picker
Permite seleccionar fechas.
https://www.npmjs.com/package/@react-native-community/datetimepicker
Expo Image Picker
Permite seleccionar imágenes de la galería de fotos.
https://www.npmjs.com/package/expo-image-picker
React native modal
Cargar un cuadro de diálogo.
React native vector icons
Permite incrustar iconos en nuestra aplicación pertenecientes a múltiples colecciones de iconos.
Font awesome
$ npm i --save react-native-svg # **
$ npm i --save @fortawesome/fontawesome-svg-core
$ npm i --save @fortawesome/free-solid-svg-icons
$ npm i --save @fortawesome/react-native-fontawesome
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCoffee } from '@fortawesome/free-solid-svg-icons'
const element = <FontAwesomeIcon icon={faCoffee} />
ReactDOM.render(element, document.body)
Persistiendo la información
https://react-native-async-storage.github.io/async-storage/docs/install/
Podemos usar el siguiente componente para gestionar el valor:
import AsyncStorage from '@react-native-async-storage/async-storage';
export const storeValue = async (value, KEY) => {
if (!KEY || typeof KEY !== 'string') return
try {
switch (typeof value) {
case 'object':
if (!Array.isArray(value))
value = JSON.stringify(value);
break;
case 'boolean':
value = value ? 'true' : '';
default:
break;
}
await AsyncStorage.setItem(KEY, value);
} catch (e) {
return console.log("ERROR_STORING_VALUE")
}
}
export const getStoredValue = async (KEY, type = '') => {
if (typeof KEY !== 'string') return console.log("ERROR_GETTING_STORED_VALUE")
try {
let value = await AsyncStorage.getItem(KEY);
if (value !== null) {
switch (type) {
case 'object':
value = JSON.parse(value);
break;
case 'boolean':
value = value === 'true';
break;
default:
break;
}
return value;
}
} catch (e) {
return console.log("ERROR_GETTING_STORED_VALUE")
}
}
export { AsyncStorage }
Trabajo con mapas: calcular distancias, ver si un punto cae dentro de cierto radio…
https://www.npmjs.com/package/geolib
Compilar con Expo
expo build:android