Adentrarse en el mundo del desarrollo móvil nos muestra que es un espacio completamente diferente. Si bien al hacer aplicaciones móviles usando frameworks híbridos como Ionic o React Native podemos usar tecnologías web que conocemos de toda la vida(CSS y JS), las condiciones para desarrollar cambian por la plataforma en la que se ejecutan.
En este artículo y el siguiente veremos un poco más de estas particularidades al desarrollar aplicaciones móviles usando React Native.
Guardando Información en Firebase
Recordemos primero cómo quedó configurada la integración con Firebase:
import * as firebase from 'firebase'
const config = {
apiKey: 'APIKEY',
authDomain: 'DOMINIO.firebaseapp.com',
databaseURL: 'https://DOMINIO.firebaseio.com',
projectId: 'PROJECTID',
storageBucket: 'DOMINIO.appspot.com',
messagingSenderId: 'SENDERID'
}
firebase.initializeApp(config)
export const database = firebase.database()
export const auth = firebase.auth()
export const storage = firebase.storage()
Tenemos la constante database
y storage
para poder acceder a la base de datos y la constante auth
para tener acceso al usuario que inició sesión.
Siendo bastante novato en React Native hay cosas las cuales no tenía idea de cómo hacer. Una de esas resultó ser el manejo de formularios. Veremos que en RN no hay formularios como tal. Lo que sí hay es un conjunto de componentes que te permiten emular uno.
Para el caso de MotoApp, emulamos un formulario usando el componente de RN que se puede usar como wrapper o contenedor de todo lo que se necesite: <View />
Así quedó el formulario, en rasgos generales:
<View>
<Text></Text>
<TextInput></TextInput>
<View>
<DatePicker />
</View>
<TextInput></TextInput>
<Button></Button>
</View>
Ya con esto es posible ir notando como ciertas cosas de la web no van a estar disponibles, no obstante, al momento de escribir el código no son tan visibles.
Una de esas cosas es validar campos.
Para React hay una librería llamada Formik que tiene muchas cosas para trabajar formularios pero para React Native no encontramos nada similar. En todo caso, como esto es un proyecto pequeño, de aprendizaje y del cual no teníamos altas pretenciones, decidimos implementar manualmente una forma de validar los campos.
Al fin y al cabo solo son dos campos.
Lo hicimos así:
// ActivityForm.js
import React, { Component } from 'react'
import { View, Text, StyleSheet } from 'react-native'
import { Appbar, TextInput, Button } from 'react-native-paper'
import { database, auth } from '../firebase'
import DatePicker from './DatePicker'
class ActivityForm extends Component {
constructor () {}
state = {
currentUser: null,
name: null,
date: "",
value: null,
id: null,
errorMessage: null
}
handleValueInput () {
const { date, value } = this.state
let badState = false
if (!value) {
this.setState({
errorMessage: "Campo Valor es requerido"
})
badState = true
}
if (!date) {
this.setState({
errorMessage: "Campo Fecha es requerido"
})
badState = true
}
return badState
}
}
Sabemos que no es la mejor forma pero para los propósitos de MotoApp bastó y sobró.
Nos valemos de un formulario que emula los denominados controlled components al cambiar la variable que representa cada campo en el estado en el evento onChange
de cada uno.
There are two main ways of handling forms in React, which differ on a fundamental level.
If the data is handled by the DOM, we call them uncontrolled components if the data is handled by the components we call them controlled components.
The React Handbook, Flavio Copes
Ahora, pasamos de resolver un problema para entrar a resolver otro ¿cómo enviar la información a Firebase?
Para ello va a ser importante el objeto auth
que se menciona al inicio del artículo.
Resulta que como necesitamos guardar la información por usuario, debemos indicar una estructura en Firebase para almacenar sus datos. Lo hacemos mediante el objeto currentUser
tomado de auth
:
import { database, auth } from '../firebase'
class ActivityForm extends Component {
constructor () {}
state = {
currentUser: null,
name: null,
date: "",
value: null,
id: null,
errorMessage: null
}
componentDidMount () {
const { navigation } = this.props
const defaultTrip = {
name: null,
date: Date.now(),
value: "0",
id: null,
}
const trip = navigation.getParam('trip', defaultTrip)
const { currentUser } = auth
this.setState({ currentUser, ...trip })
}
}
Ya teniendo este objeto podemos definir la estructura al momento de guardar en Firebase:
import { database, auth } from '../firebase'
class ActivityForm extends Component {
constructor () {}
state = {}
handleSave () {
if (this.handleValueInput())
return
const { name, date, value } = this.state
let tripKey = null
/**
* if id is present, that means a trip was passed in to be edited
* otherwise, we generate a key in order to create a brand new trip
*/
if (this.state.id) {
tripKey = this.state.id
} else {
const newActivityRef = database.ref().child('activities').push()
tripKey = newActivityRef.key
}
let toSave = {}
toSave[`/user-activities/${this.state.currentUser.uid}/${tripKey}`] = {
name,
value,
date: new Date(date).getTime()
}
database
.ref()
.update(toSave)
.then(() => this.props.navigation.navigate('Main'))
.catch((error) => console.log(error))
}
}
Del código anterior lo clave es:
const newActivityRef = database.ref().child('activities').push()
tripKey = newActivityRef.key
let toSave = {}
toSave[`/user-activities/${this.state.currentUser.uid}/${tripKey}`] = {
name,
value,
date: new Date(date).getTime()
}
database
.ref()
.update(toSave)
.then(() => this.props.navigation.navigate('Main'))
.catch((error) => console.log(error))
}
Que es la forma en que se determina la estructura, se indica la referencia en base al identificador único del usuario en la base de datos de tipo documento y finalmente se guarda al hacer database.ref().update(toSave)
.
Y ahora, ¿Cómo editamos?
La edición reutiliza el mismo componente de formulario ActivityForm.js
pero en el listado de actividades hay un cambio menor.
Resulta que el componente View
de React Native no soporta el evento onPress
y si no se quiere implementar un menú de tres puntos verticales a un lado de cada elemento, se puede hacer uso del componente TouchableOpacity
el cual sí soporta onPress
.
export default class Main extends React.Component {
renderTrips() {
return (
<SectionList
renderItem={({item, index, section}) => (
<TouchableOpacity
style={styles.tripDetail}
onPress={() => this.editTrip(item)}
>
<Text style={styles.tripName}>{item.name}</Text>
<Text style={styles.tripPrice}>{currency(item.value)}</Text>
</TouchableOpacity>
)}
...
</SectionList>
)
}
}
Y así, con un poco de paciencia, buscando y probando, pudimos mandar información a Firebase desde una aplicación móvil creada en React Native.
En un próximo artículo estaremos contando otros detalles sobre esta experiencia y los pasos para empezar ha publicar la aplicación y que sea descargable/usable por otras personas.