Curso de React Native

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.

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 />
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;
`;

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.

Cargaremos una librería externa para hacerlo.

React native vector icons

Permite incrustar iconos en nuestra aplicación pertenecientes a múltiples colecciones de iconos.

https://www.npmjs.com/package/react-native-vector-icons

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
Curso de React Native →

Aviso Legal | Política de privacidad