React Native es la tecnología de Facebook liberada desde 2015 para el desarrollo de aplicaciones móviles multiplataforma (Android e iOS, incluso Web), mientras Expo es un marco de trabajo que opera sobre React Native para facilitar el desarrollo con esta tecnología. Básicamente Expo evita el uso de código específico para cada plataforma cuando accedes a ciertas características (notificaciones push, cámara, contactos, etc.) permitiendo que el desarrollador se concentre en el código Javascript
.
Entre las caractarísticas distintivas de React Native es que usa los mismos principios de la librería React pero las aplicaciones móviles quedan nativas, con Expo obtienes la facultad de usar un simple editor de texto en lugar de un IDE y se posibilita la carga fluida de la aplicación (menos espera). Expo soporta Android 5+ e iOS 10+, también es posible programar para la Web, motivos suficientes para despertar interés.
OnMind usa esta tecnología a nivel visual (Front-End) para aplicaciones móviles introduciéndose gradualmente conforme a nuestras necesidades y enfoque, dado que hasta una aplicación móvil híbrida puede ser suficiente en nuestro caso variando según algún se requiera en un proyecto específico.
Antes de iniciar con éste marco de trabajo, debemos asegurarnos de tener un entorno preparado para su uso. Básicamente un editor de código como VSCode y un dispositivo móvil, además de tener instalado
Node.js
.
choco install -y nodejs.install
En resumen, se deben ejecutar los siguientes comandos (donde project
es el nombre de la carpeta del proyecto):
npm install expo-cli --global
expo init project --template expo-template-blank
cd project
expo start
Ahora simplementes abres la carpeta del proyecto (
project
) con un editor de código (como VSCode) y editas el archivoApp.js
.
En la línea del comandoexpo init
se puede omitir--template expo-template-blank
.
Conexpo start
es posible leer un código QR con la cámara de nuestro móvil para cargar facilmente nuestra aplicación en el dispositivo, evitando la fatiga de los emuladores.
Para implementar nuestro propio código, sustituimos el contenido del archivo App.js
por el siguiente:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<Text>Hi there!</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Esta vez, recuerda ejecutar nuevamente el comando
expo start
.
Nótese que algo distintivo de React Native (o React) es que retorna un trozo dehtml
dentro de un paréntesis()
de modo natural. Este formato se denominaJSX
(como si fuese una extension dejavascript
).
Para nuestro ejercicio, debemos ejecutar adicionalmente el siguiente comando:
expo install react-native-webview
Agreguemos algunas características reemplazando el contenido de nuestro código por el siguiente:
import React from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
export default function App() {
return (
<View style={{ flex: 1 }}>
<WebView
allowFileAccess
originWhitelist={ ['*'] }
source={{ uri: 'https://www.wikipedia.org/' }}
style={{ marginTop: 20 }}
/>
</View>
);
}
Puedes probar cambiando la
uri
por una propia. De hecho, podrías usar arhivos locales estableciendouri
:(Platform.OS == "android" ? 'file:///android_asset/www/index.html' : './resources/www/index.html')
Si se ha cancelado el proceso anterior, se ejecuta nuevamente el comandoexpo start
.
Para generar un paquete distribuible para Android abres una nueva terminal y ejecutas el comando:
expo build:android -t apk
Esta accion requiere que se registre una cuenta de usuario con Expo y tener configurado en el archivo app.json
lo correspondiente a la plataforma Android. Por ejemplo:
"owner": "user",
"orientation": "default",
...
"android": {
"package": "com.example.project"
}
El comando
expo build:android
nos pregunta si cargamos unakeystore
o dejamos a Expo generarla por nosotros (aplicamos a esta opción). Indicar en el archivoapp.json
el usuario registrado en Expo comoowner
también es parte de la configuración.
Resumimos entonces los comandos utilizados hasta ahora con Expo
en el siguiente listado:
expo init ...
expo install ...
expo start
expo build:androd
Tanto React
(liberada a mediados del año 2013) como React Native
son librerías para la interfaz de usuario (UI
) que han tenido gran acogida teniendo actualmente la mayor popularidad en su contexto. React Native
se diferencia principalmente de React
al enfocarse en el desarrollo móvil nativo y multi-plataforma a partir de la misma lógica y el uso de jsx
(que combina html
dentro de javascript
como una modalidad extendida), posibilitando el acceso a recursos del dispositivo y usando sus propias etiquetas bien definidas (tales como Text
, View
, WebView
, etc.).
Aunque en los ejercicios vistos se ha usado la simplicidad de la programación funcional (que se promueve actualmente con el uso de Hooks
), puedes encontrar código en React
o React Native
que implementa clases para definir el componente web. En últimas, un componente web se traduce en etiquetas personalizadas desde el punto de vista de html
.
Para programar con estas librerías se requiere Node.js
(adicionalmente Webpack
) y se puede iniciar un proyecto de React
directamente con la siguiente sentencia (donde project
es el nombre de la carpeta del proyecto):
npx create-react-app project
Esto crea una estructura para aplicación basada en la plantilla y hace uso interno de Webpack
al momento de su despliegue. Lo más relevante aquí es que React
usa un archivo index.html
que invoca al index.js
que a su vez carga el componente de App.js
(en un DOM Virtual). El archivo index.html
puede verse de la siguiente manera:
<body>
<div id="root"></div>
</body>
El archivo index.js
puede verse de la siguiente manera:
import React from `react`;
import ReactDOM from `react-dom`;
import App from `./App.js`;
ReactDOM.render(<App />, document.getElementByid('root'));
El archivo App.js
basado en clases, puede verse de la siguiente manera:
export default class App extends React.Component {
render() {
return (<h1>Hi there!</h1>);
}
}
O escrito como función, puede verse así:
export default function App() {
return (<h1>Hi there!</h1>);
}
En el sentido esencial, React es una librería para la UI. Usa la sintaxis
JSX
, la cual consiste en trozos deHTML
dentro deJavascript
para obtener componentes web, que se definen mediante funciones. Para esto se debe considerar que:
for
,map
) y las validaciones (if
) se aplican usando Javascript
, mientras los trozos HTML
se establecen con paréntesis y return
(o render)HTML
, encontrando el estándar camel-case: onClick
, onChange
, onKeyPress
, onSubmit
HTML
, encontrando: className
, htmlFor
, dangerouslySetInnerHTML
HTML
se usan: props (propiedades) y state (estado)hooks
(métodos vinculados al renderizado de un componente), siendo esencial: useState
, useEffect
, useContext
style
) se expresa en JSON
con otra llave {{...}}
(cuando se usa directamente). De hecho, las expresiones usan llaves {...}
Para una noción general o temario sugerido, se puede continuar investigando o abordando tanto React
como React Native
con los siguientes tópicos:
props
)state
)constructor
render
componentDidMount
componentWillUnmount
getDerivedStateFromProps
shouldComponentUpdate
componentDidUpdate
onClick
, onChange
, onKeyPress
, onSubmit
, etc.)useState
(gestiona estado asignando un valor y la función que lo actualiza: const [state,setState] = useState({})
)useEffect
(cubre componentDidMount
, componentDidUpdate
, and componentWillUnmount
con funciones, el primero con un segundo parámetro así: []
)useContext
useRef
useMemo
useCallback
React Context
)React Router
). Y como propuesta visual agregada para React Native
, el uso de componentes basados en el proyecto NativeBase
.Si recientemente te haz introducido en
React
o no tienes conocimiento aún y piensas hacerlo, te sugiero seguir el nuevo camino conHooks
en lugar del ciclo de vida clásico de las clases, incluso para proyectos nuevos si te consideras experto. LosHooks
son promovidos actualmente por Facebook, sólo que proyectos y ejemplos anteriores a 2020 eran programados con clases y por ello aún son soportadas, así que es un “plus” concocer sobre éstas.
A continuación veamos como crear un proyecto directamente con la CLI
de React Native. En este escenario es preciso instalar Android Studio y configurar su SDK
. Además debemos tener instalado Python2
y JDK 8+
(si no se ha instalado antes). Si tienes instalado chocolatey
en Windows, para lo último mencionado puede usarse una terminal en Windows con privilegios de Administrador y ejecutar:
choco install -y python2 openjdk8
En cuanto a Android Studio, puedes descargarlo desde el sitio oficial.
Una vez se cuenta con el entorno debidamente preparado, nos ubicamos en una carpeta del espacio de trabajo o folder anterior al proyecto que crearemos y ejecutamos:
npx react-native init project
Luego abrimos la carpeta creada por la CLI
de React Native, usamos por primera vez el iniciador del proyecto y lanzamos el emulador para Android, así:
cd project
npx react-native start
npx react-native run-android
Este último comando es el que seguiremos usando para visualizar nuestra aplicación. Podemos modificar alguna parte del texto, guardamos y veremos que los cambios se reflejan en el emulador (o el dispositivo conectado por USB).
Lo que haremos a continuación es un proyecto con WebView
pero con archivos locales (o estáticos) pensando en una aplicación offline
que no dependería del acceso a internet para operar. Para esto, primero debemos detener el proceso (CTRL-C
) para instalar el módulo requerido con el siguiente comando:
npm install react-native-webview
Es importante tener en cuenta que para nuestro ejercicio con Android, se deben colocar los archivos estáticos (
index.html
ymain.js
que veremos más abajo) en la rutaandroid/app/src/main/assets/
(relativo al proyecto).
Veamos primero el código del archivo App.js
en React Native:
import React, { useState, useEffect } from 'react';
import { View, Platform } from 'react-native';
import { WebView } from 'react-native-webview';
const App: () => React$Node = () => {
const [source, setSource] = useState({ html: '<strong>Loading...</strong>' });
useEffect(() => { // Something after render it
if (Platform.OS !== 'ios') // Android & Web
setSource({ uri: 'file:///android_asset/index.html' });
else // iOS
setSource('./resources/index.html');
}, []);
return (
<View style={{ flex: 1 }}>
<WebView
allowFileAccess
originWhitelist={['*']}
source={source}
javaScriptEnabled={true}
domStorageEnabled={true}
/>
</View>
);
};
export default App;
Para los archivos index.html
y main.js
tendremos el siguiente código respectivamente:
<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WebView & Local Files</title>
</head>
<body>
<div id="root"><h1>My App</h1></div>
<script defer src="main.js"></script>
</body>
</html>
(() => {
document.getElementById('root').innerHTML += 'Yeah! ~> from Javascript.';
})();
Si haces un ejercicio distinto en dónde tienes un
script
generado porwebpack
y no funciona, podría sospecharse de que el código que se tenga enjavascript
no sea soportado en alguna parte (o quizás la versión de Android no soporte ese código). Puedes validar con este simple ejemplo para tener un referente fiable, es decir, confirmar que al menos éste código si funciona.
La carga en caliente (hot-reload
) no sirve de mucho con archivos estáticos, por lo que si modificas algo de html
o javascript
no lo asumirá el emulador inmediatamente, es decir, debe cancelarse el proceso (CTRL-C
) y ejecutarse de nuevo npx react-native run-android
para que vuelva a generar el binario de la plataforma Android. Distinto si colocas el código como cadenas dentro de React Native. Veamos el siguiente ejemplo reescribiendo toda nuestra aplicación, así:
import React from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
const App: () => React$Node = () => {
const js = `
(() => {
document.getElementById('root').innerHTML += 'Yeah! ~> from Javascript.';
})();
`;
const html = `<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WebView & Local Files</title>
</head>
<body>
<div id="root"><h1>My App</h1></div>
<script>${js}</script>
</body>
</html>`;
const source = { html: html };
return (
<View style={{ flex: 1 }}>
<WebView
allowFileAccess
originWhitelist={['*']}
source={source}
javaScriptEnabled={true}
domStorageEnabled={true}
/>
</View>
);
};
export default App;
También puede implementarse con Expo teniendo en cuenta que la línea dónde se define y exporta la aplicación tendría la siguiente presentación:
export default function App() {
...
}
Si una aplicación es puramente web con elementos estáticos (
html
yjs
) y no se usan recursos nativos (o quizás alguno muy específico), es preferible quedarse con laCLI
de React Native en lugar de Expo, salvo por el flujo de despliegue de aplicación que ofrece esta última tecnología que incorpora la publicación en las tiendas respectivas, siendo un factor diferenciador. Debe tenerse en cuenta que Expo puede cargar más lógica para ofrecer un acceso más sencillo sobre las características nativas.
Con esto hemos cubierto los primeros pasos aprovechando conocimientos de desarrollo web, pero las aplicaciones nativas no suelen usar el componente WebView
(que proporciona mas bien algo hibrido) sino los componentes para la interfaz nativa, así que para continuar en esa línea se debe explorar React Native como tal. Esto es tan solo un punto de partida o una perspectiva para el desarrollo móvil con tecnologías web convencionales o puras.
¿Qué es una aplicación móvil híbrida?: Tomando lo visto, podría decirse que es una aplicación web que usa el componente
WebView
para la interfaz de usuario en móviles y puede tener acceso a características nativas. El tiempo de respuesta respecto a los componentes nativos disminuye pero esto podría ser poco significativo si no se tienen exigencias sobre la interfaz de usuario y se valora conservar buena parte del código. Puede ser el caso de software de gestión que simplemente requiere presentar datos o una página web (estática) con diseño responsivo que puede envolverse o presentarse como una “app”.
Si queremos reorganizar o refactorizar el código anterior, podemos tener un archivo separado con las cadenas que representan el contenido web estático. Es decir, tendríamos los archivos App.js
y web.js
respectivamente así:
import React from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
import web from './web';
const App: () => React$Node = () => {
return (
<View style={{ flex: 1 }}>
<WebView
allowFileAccess
originWhitelist={['*']}
source={{ html: web }}
javaScriptEnabled={true}
domStorageEnabled={true}
/>
</View>
);
};
export default App;
const js = `
(() => {
document.getElementById('root').innerHTML += 'Yeah! ~> from Javascript.';
})();
`;
const html = `<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>WebView & Local Files</title>
</head>
<body>
<div id="root"><h1>My App</h1></div>
<script>${js}</script>
</body>
</html>`;
export default web;
Este último bloque de código correspondería al nuevo archivo llamado web.js
. Revisa de nuevo, el resultado seguirá siendo el mismo pero nuestro código luce mejor.
Espero se haya logrado una prueba de concepto y un panorama para saber elegir el camino a seguir si requieres aplicar estos conceptos en un proyecto específico.
NativeBase es un proyecto de tecnología enfocado en el estilizado visual de los componentes que simplifica la declaración de estilos y se basa en ReactNative, además puede ser utilizado con Expo. En otras palabras, con las tres tecnologías mencionadas consigues aplicaciones móviles dóndole cierto tipo de apariencia con una declaración más simple o clara, y logrando acceder a características del dispositivo con las capacidades que brinda Expo. También puede usarse para la Web.
Para comenzar con NativeBase, teniendo instalado previamente Expo, nos ubicamos desde la línea de comandos en una carpeta que se haya destinado como espacio de trabajo para proyectos de código, y desde allí ejecutamos el siguiente comando:
expo init proyect --template @native-base/expo-template
cd project
expo upgrade
npm start
project
se refiere al nombre asignado para un proyecto.
Para desarrollar, podemos usar como editor VSCode y la extensión para NativeBase
Al abrir el proyecto con un editor, encontraremos el archivo App.js
con la función App
, que tendría un esquema de código como el siguiente:
export default function App() {
return (
<NativeBaseProvider>
...
</NativeBaseProvider>
);
}
Notése que el componente
NativeBaseProvider
envuelve los componentes como proveedor.
Lo que haremos es buscar una imágen que tengamos en formato png
con un tamaño mediano o apropiado para hacer un cambio a las siguientes líneas:
<NativeBaseIcon />
<Heading size="lg">Welcome to NativeBase</Heading>
Luego de identificar la imágen png
y renombrarla a icon.png
, sustituimos las líneas de código anteriores por las siguientes:
<Image source={require('./assets/icon.png')} alt="Icon" />
<Heading size="lg">Welcome to "The Jungle"</Heading>
∫
Pero para que el anterior código funcione, debemos importar Image
de la librería native-base
. Por ejemplo:
import { Image, Center, NativeBaseProvider } from "native-base";
Lo anterior quiere sugerir que se debe incluir
Image
en la línea deimport
respectiva.
Si fuera necesario reiniciar el proceso, ejecutaríamos de nuevo npm start
y observamos los cambios. También puedes probar modificando el enlace del ejemplo para que se dirija a tu Blog o sitio de preferencia. Y para generar nuestro archivo distribuible para Android
usamos el comando:
npm i -g eas-cli
eas build -p android