Haciendo MotoApp: Guardando Información en Firebase y Formularios en React Native

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&gt;
  <Text&gt;</Text&gt;
  <TextInput&gt;</TextInput&gt;
  <View&gt;
    <DatePicker /&gt;
  </View&gt;
  <TextInput&gt;</TextInput&gt;
  <Button&gt;</Button&gt;
</View&gt;

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}) =&gt; (
          <TouchableOpacity
            style={styles.tripDetail}
            onPress={() =&gt; this.editTrip(item)}
          &gt;
            <Text style={styles.tripName}&gt;{item.name}</Text&gt;
            <Text style={styles.tripPrice}&gt;{currency(item.value)}</Text&gt;
          </TouchableOpacity&gt;
        )}
      ...
      </SectionList&gt;
    )
  }
}

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.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.