ACTUALIZACIÓN: actualizamos este tutorial para
el Firebase 3.1.0 SDK, que ahora admite la base de datos en tiempo real y la autenticación de Firebase.
En Firebase,
somos grandes seguidores de React. Firebase sincroniza el estado de las aplicaciones y React representa nuevamente la IU de estas según los cambios de estado. Es un complemento perfecto.
Con React Native, el desarrollo de aplicaciones se convirtió en un proceso mucho más sencillo para los desarrolladores de JavaScript. Gracias a React Native, puedes crear aplicaciones nativas reales usando únicamente JavaScript. Eso es asombroso. Vivimos en el futuro. Comencemos.
Configuración
Si deseas ir directamente al código,
puedes visitar el repositorio definitivo de GitHub aquí. Si no, veamos el proceso paso a paso.
Dar los primeros pasos con React Native es bastante sencillo, pero existen algunos aspectos que deberías conocer. Si ya cuentas con React Native, puedes omitir esta sección.
En primer lugar, necesitarás
Homebrew, que es fácil de instalar. También necesitarás
Node.js 4.0 o versiones posteriores. El equipo de React Native recomienda usar
nvm para administrar las versiones de tus nodos (yo también)..
Una vez instaladas esas herramientas, ejecuta los siguientes comandos:
brew install watchman
npm install -g react-native-cli
Por último, podrás iniciar tu proyecto con el comando de CLI:
react-native init GroceryApp # or whatever you want
Abre la carpeta principal en tu editor favorito.
atom GroceryApp # if you’re into Atom
Compilación y ejecución
Para compilar un proyecto de React Native, ejecuta el siguiente comando:
react-native run-ios
Con esto deberá iniciarse el simulador y verás la pantalla de código estándar.
React Native viene con
recarga directa. Esto significa que puedes editar el código, index.ios.js, y luego seleccionar
Cmd+R
para ver al instante una actualización con los cambios incluidos. Si eres desarrollador de iOS o, de hecho, de cualquier plataforma, sabrás cuán positivo es esto.
Una vez configurada la compilación, pondremos Firebase en funcionamiento.
Configuración de Firebase
React Native administra dependencias a través de
npm
. Para instalar Firebase, ejecuta el siguiente comando en la raíz del proyecto.
npm install firebase --save
Abre
index.ios.js
y agrega la siguiente línea en la parte superior:
import * as firebase from 'firebase';
Luego, encima del componente, inicializa Firebase con tus valores de configuración:
// Initialize Firebase
const firebaseConfig = {
apiKey: "<your-api-key>",
authDomain: "<your-auth-domain>",
databaseURL: "<your-database-url>",
storageBucket: "<your-storage-bucket>",,
};
const firebaseApp = firebase.initializeApp(firebaseConfig);
¿Qué es un elemento const
? Un elemento
const
es una referencia de solo lectura a un valor, lo cual tiene sentido aquí porque no deseas sobrescribir nunca el valor de Firebase. Debido a que usas Node 4.0 o una versión posterior, puedes emplear esta palabra clave. Si tu editor se vuelve loco, te mentirá.
Con una función de ES2015 no es suficiente. En lugar de usar
React.createClass()
para definir componentes, usaremos clases.
Componentes de ES2015 (ES6)
React se basa en componentes. Esto significa que una aplicación consiste simplemente en un árbol con componentes que parte de un componente raíz.
A partir de React 0.14, puedes usar clases de ES2015 para definir componentes de React.
En
index.ios.js
, cambiaremos el componente para usar una clase en lugar de
React.createClass()
.
class GroceryApp extends Component {
render() {
return (
<View style="{styles.container}">
</View>
);
}
}
¿Por qué clases de ES2015 en lugar de
React.createClass()
?
Aunque el asunto suscita importantes debates, todo se resume a una cuestión de gusto.
El shell de la aplicación quedará así completo. Haremos que se vea bien o, al menos, medianamente decente.
Estilos en React Native
En React Native se usa JavaScript en lugar de hojas de estilos en cascada (CSS) para los estilos. Esto puede dar la impresión de una divergencia extrema, pero en realidad no es tan así. Para declarar un conjunto de estilos, crea un elemento
StyleSheet
.
var styles = StyleSheet.create({
container: {
backgroundColor: '#f2f2f2',
flex: 1,
},
});
Un elemento
StyleSheet
contiene un conjunto de objetos que representan estilos similares a los de CSS. Luego usa estos estilos en un componente de React:
<View style="{styles.container}">
I’m a container lol!
</View>
Por lo tanto, no deberás preocuparte por la posibilidad de perder tus capacidades de CSS. En todo caso,
te convendrá aprender sobre flexbox de CSS para agilizar mucho el manejo de estilos en React Native.
Ahora que eres un profesional de los estilos en React, declararemos los estilos para la aplicación.
Adición de estilos
Crea un archivo con el nombre
styles.js
y
agrega el siguiente código de este archivo. Estos serán los estilos que usaremos para la aplicación.
Observarás que en React Native se usan módulos CommonJS. En la parte inferior de
styles.js
, StyleSheet se exporta usando
module.exports
.
Esto te permitirá importar estos estilos usando
require()
. Abre
index.ios.js
y agrega la siguiente línea de código:
const styles = require('./styles.js')
Asegúrate de eliminar la variable de estilos de la parte inferior del archivo.
De esta manera, los estilos quedarán listos. Veamos la estructura de componentes de la aplicación.
Desglose de la IU en una jerarquía de componentes
El mejor consejo que leí sobre el uso de React
es el de comenzar por desglosar la UI en una jerarquía de componentes. A continuación, se ofrece una descripción de la jerarquía de componentes de la aplicación.
La aplicación consta de cinco componentes:
- GroceryApp (naranja): contiene toda la aplicación.
- StatusBar (púrpura): muestra el título de la vista.
- ListView (verde): muestra la lista de artículos de supermercado.
- ListItem (negro): muestra un elemento individual de la lista.
- ActionButton (azul): agrega un elemento a la lista.
Crea una carpeta llamada
components
. Cada uno de estos componentes se almacena en la carpeta de componentes.
GroceryApp
es la excepción, ya que está contenido en
index.ios.js
.
Agrega cada uno de los siguientes componentes a la carpeta
components
:
ActionButton.js
'use strict';
import React, {Component} from 'react';
import ReactNative from 'react-native';
const styles = require('../styles.js')
const constants = styles.constants;
const { StyleSheet, Text, View, TouchableHighlight} = ReactNative;
class ActionButton extends Component {
render() {
return (
<View style={styles.action}>
<TouchableHighlight
underlayColor={constants.actionColor}
onPress={this.props.onPress}>
<Text style={styles.actionText}>{this.props.title}</Text>
</TouchableHighlight>
</View>
);
}
}
module.exports = ActionButton;
ListItem.js
import React, {Component} from 'react';
import ReactNative from 'react-native';
const styles = require('../styles.js')
const { View, TouchableHighlight, Text } = ReactNative;
class ListItem extends Component {
render() {
return (
<TouchableHighlight onPress={this.props.onPress}>
<View style={styles.li}>
<Text style={styles.liText}>{this.props.item.title}</Text>
</View>
</TouchableHighlight>
);
}
}
module.exports = ListItem;
StatusBar.js
'use strict';
import React, {Component} from 'react';
import ReactNative from 'react-native';
const styles = require('../styles.js')
const { StyleSheet, Text, View} = ReactNative;
class StatusBar extends Component {
render() {
return (
<View>
<View style={styles.statusbar}/>
<View style={styles.navbar}>
<Text style={styles.navbarTitle}>{this.props.title}</Text>
</View>
</View>
);
}
}
module.exports = StatusBar;
Una vez agregados los componentes, haremos una versión estática de la aplicación.
Prototipo estático
En
index.ios.js
, agrega las siguientes importaciones a la parte superior de la página:
import React, {Component} from 'react';
import ReactNative from 'react-native';
import * as firebase from 'firebase';
const StatusBar = require('./components/StatusBar');
const ActionButton = require('./components/ActionButton');
const ListItem = require('./components/ListItem');
const styles = require('./styles.js');
Luego agrega el siguiente snippet de código:
_renderItem(item) {
return (
<ListItem item="{item}" onpress="{()" ==""> {}} />
);
}
render() {
return (
<View style="{styles.container}">
<StatusBar title="Grocery List">
<ListView datasource="{this.state.dataSource}" renderrow="{this._renderItem.bind(this)}" style="{styles.listview}/">
<ActionButton title="Add" onpress="{()" ==""> {}} />
</View>
);
}
La función
render()
es la vista principal de la aplicación y
_renderItem()
establece los elementos individuales de la lista.
Luego crea un constructor para el componente raíz,
GroceryApp
.
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
};
}
El componente tiene una propiedad especial llamada
state
, que administra todo el flujo de datos de la aplicación. Veremos esto en mayor profundidad en la sección siguiente.
El estado de la aplicación es un elemento
ListView.DataSource
, una clase que proporciona procesamiento eficaz de datos a un componente
ListView
. El siguiente paso consiste en mostrar estos datos.
Cada componente tiene un ciclo de vida en el cual se llama a determinadas funciones ante eventos importantes. Cuando un componente se representa por primera vez, se llama a
componentDidMount()
. Aquí conviene configurar cualquier estado inicial de la aplicación.
componentDidMount() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows([{ title: 'Pizza' }])
})
}
Tras crear y ejecutar la aplicación, verás la siguiente aplicación estática.
Cada uno de estos componentes simplemente muestra datos o fija una función de callback para un toque.
El aspecto clave que debes comprender es que estos componentes no tienen estado. Tienen propiedades que se fijan a través de sus componentes raíces,
GroceryApp
. Para comenzar a comprender React, debes aprender a administrar el estado.
Estado
El estado simplemente consiste en datos que pueden modificarse. Se denomina “estado” a estos datos porque representan el estado de tu aplicación. Si estos datos cambian, es probable que lo mismo suceda con el aspecto de tu aplicación y que, por lo tanto, su “estado” sea diferente. El estado generalmente abarca aspectos como una lista de tareas o un botón de habilitación o inhabilitación.
El componente raíz servirá como portador del estado. Los cambios de estado comienzan en el componente raíz, que luego debe actualizar las propiedades de sus componentes secundarios.
Las propiedades de los componentes son inmutables. Esto significa que no pueden modificarse.
Por lo tanto, si no es posible modificar las propiedades... ¿cómo es posible que cambien? Se debe representar de nuevo la aplicación llamando a
setState()
, como se observa en
componentDidMount()
.
La función
setState()
es especial porque cada vez reciba llamadas intentará volver a representar por completo la aplicación. Esto significa que si la propiedad de un campo secundario es diferente respecto del último estado, se volverá a representar con el nuevo valor.
Aquí es donde React se combina con Firebase a la perfección. Porque la base de datos de Firebase sincroniza estados de las aplicaciones en varios dispositivos y React vuelve a representar con eficacia cambios de estado de las aplicaciones.
Receptor de la base de datos en tiempo real
Crea la referencia de la base de datos en tiempo real como una propiedad en el constructor:
this.itemsRef = firebaseApp.database().ref();
Luego agrega la siguiente función al componente
GroceryApp
:
listenForItems(itemsRef) {
itemsRef.on('value', (snap) => {
// get children as an array
var items = [];
snap.forEach((child) => {
items.push({
title: child.val().title,
_key: child.key
});
});
this.setState({
dataSource: this.state.dataSource.cloneWithRows(items)
});
});
}
Esta función crea un receptor de valores para todos los artículos de supermercado. Cuando se agregue, modifique o elimine un artículo, obtendrás de nuevo el conjunto de resultados completo, bajo la forma de un elemento
DataSnapshot
, a través del Firebase SDK. Usando
DataSnapshot
, debes llamar a
forEach(child)
, que se itera en todos los campos secundarios y los agrega a un arreglo como un artículos de supermercado. Observarás que en la función
.forEach
se crea una propiedad
_key
a partir del valor
.key()
de DataSnapshot. Esto simplificará mucho las operaciones de datos posteriormente.
Una vez que se complete el arreglo, actualizarás la propiedad dataSource en el estado usando
dataSource.cloneWithRows(items)
. La función
cloneWithRows()
es simplemente un método práctico para crear un nuevo elemento
ListView.DataSource
basado en el mismo elemento
DataSource
definido previamente.
Luego, escribirás el elemento
componentDidMount()
de límite de la recepción:
componentDidMount() {
this.listenForItems(this.itemsRef);
}
Compila y ejecuta la app. Aparecerá una página vacía, pero puedes intentar agregar algunos elementos usando el panel de aplicaciones de Firebase o el
superfabuloso visor de datos Vulcan. ¡Verás la página actualizarse en tiempo real!
Esto es increíble. Sin embargo, asegúrate de que el botón “Add” funcione. Veremos eso en la sección que sigue.
Adición de artículos
Al tocar
ActionButton
, aparecerá una alerta con la cual se solicitará al usuario ingresar un artículo. Usarás la API
AlertIOS
para crear este cuadro de alerta.
Agrega la siguiente función al componente
GroceryApp
:
_addItem() {
AlertIOS.prompt(
'Add New Item',
null,
[
{
text: 'Add',
onPress: (text) => {
this.itemsRef.push({ title: text })
}
},
],
'plain-text'
);
}
A la hora de crear alertas, la API AlertIOS
ofrece bastante extensibilidad. Los primeros dos parámetros son simples. Solo representan el cuadro de alerta y un mensaje opcional. El tercer parámetro es la esencia de la API. Aquí, podrás crear un arreglo que especifique los botones disponibles para el usuario. Cada botón puede tener un elemento
text
y
style
, y una función de callback
onPress
. El último parámetro es el tipo de entrada, independientemente que sea
plain-text
o
secure-text
.
Para agregar un artículo, crea un objeto en el arreglo de botones. Este objeto puede agregar artículos en el callback de
onPress
. El callback devuelve el texto que el usuario introduce. Usa este texto para
.push()
un nuevo campo secundario en la ubicación de
/items
.
Luego, deberás actualizar
render()
para asignar la propiedad onPress de
ActionButton
:
<ActionButton title="Add" onpress="{this._addItem.bind(this)}">
</ActionButton>
Realiza la compilación y ejecuta la aplicación. Cuando toques el botón “Add” e introduzcas un título de artículo, verás la actualización en la lista.
Genial, pero... ¿en qué clase de lista de supermercado no se completan los artículos?
Compleción de artículos
El usuario puede completar un artículo tocándolo para que se abra un cuadro de alerta. Si presiona la opción “Complete”, puedes borrarla de la lista.
Modifica
_renderItem(item)
para incluir un callback
onPress
:
_renderItem(item) {
const onPress = () => {
AlertIOS.prompt(
'Complete',
null,
[
{text: 'Complete', onPress: (text) => this.itemsRef.child(item._key).remove()},
{text: 'Cancel', onPress: (text) => console.log('Cancel')}
],
'default'
);
};
return (
<ListItem item="{item}" onpress="{onPress}">
);
}
Para “completar” un artículo, deberás eliminarlo de la base de datos de Firebase. Usando
.child(key)
, puedes inspeccionar un artículo específico de la lista. El callback de
onPress
es un cierre. Por lo tanto, tiene acceso al entorno externo que contiene el parámetro
item
.
En este punto, resulta práctica la propiedad _key
.
Cuando se toca la opción “Completar”, puedes encontrar el campo secundario usando la propiedad
_key
en
item
. Luego, puedes llamar a
.remove()
para eliminar el artículo en la base de datos Firebase.
Realiza la compilación y ejecuta la aplicación de nuevo. Toca cualquier
ListItem
y luego “Completar”. Con esto, lo verás desaparecer de la lista.
Obtén el código y deja una estrella
Verifica la aplicación completa en Github. También agradeceremos muchísimo de tu parte la generosidad de colaborar con una estrella. Si lo deseas, puedes bifurcar el repositorio e incluso enviar un RP.
¿Aún tienes dificultades?
Si experimentas problemas, inicia una consulta en Stackoverflow. Verificaremos la etiqueta de Firebase en detalle o dejaremos comentarios en el
Slack Team de la comunidad.