Creación de una aplicación de chat en tiempo real con React y Firebase
- Pero primero, ¿qué es Firebase?
- Esto es lo que estamos haciendo
- configurando
- Autenticación de correo electrónico y contraseña
- Configurar la aplicación web
- Incorporemos Firebase a la aplicación
- El siguiente paso es el enrutamiento
- Comprobando la autenticación
- Registrar usuarios con correo electrónico y contraseña
- Autenticarse con una cuenta de Google
- Autenticarse con una cuenta de GitHub
- Leyendo datos de Firebase
- Escribir datos en Firebase
- ¡Hora de la demostración!
- ¡Disfruta de tu nueva aplicación de chat!
En este artículo, cubriremos conceptos clave para autenticar a un usuario con Firebase en una aplicación de chat en tiempo real. Integraremos proveedores de autenticación de terceros (por ejemplo, Google, Twitter y GitHub) y, una vez que los usuarios hayan iniciado sesión, aprenderemos cómo almacenar datos de chat de usuarios en Firebase Realtime Database, donde podemos sincronizar datos con una nube NoSQL. base de datos.
La aplicación cliente se construirá en React, ya que es uno de los marcos de JavaScript más populares que existen, pero los conceptos también se pueden aplicar a otros marcos.
Pero primero, ¿qué es Firebase?
Firebase es la plataforma móvil de Google para desarrollar aplicaciones rápidamente. Firebase proporciona un conjunto de herramientas para autenticar aplicaciones, crear aplicaciones cliente reactivadas, realizar análisis de informes y una serie de otros recursos útiles para administrar aplicaciones en general. También proporciona administración de back-end para web, iOS, Android y Unity, una plataforma de desarrollo 3D.
Firebase incluye listas de funciones para usar que ayudan a los desarrolladores como nosotros a concentrarnos en crear aplicaciones mientras manejamos toda la lógica del lado del servidor. Cosas como:
- Autenticación : esto incluye soporte para autenticación de correo electrónico y contraseña, así como capacidades de inicio de sesión única (a través de Facebook, Twitter y Google).
- Base de datos en tiempo real : Se trata de una base de datos “NoSQL” que se actualiza en tiempo real.
- Funciones en la nube : ejecutan una lógica adicional del lado del servidor.
- Alojamiento estático : esta es una forma de servir recursos prediseñados en lugar de renderizarlos en tiempo de ejecución.
- Almacenamiento en la nube: esto nos brinda un lugar para almacenar activos multimedia.
Firebase ofrece un generoso nivel gratuito que incluye autenticación y acceso a su base de datos en tiempo real. Los proveedores de autenticación que cubriremos, correo electrónico y contraseña (Google y GitHub), también son gratuitos en ese aspecto. La base de datos en tiempo real permite hasta 100 conexiones simultáneas y 1 gigabyte de almacenamiento por mes. Puede encontrar una tabla completa de precios en el sitio web de Firebase.
Esto es lo que estamos haciendo
Vamos a crear una aplicación llamada Chatty. Permitirá que solo los usuarios autenticados envíen y lean mensajes y los usuarios podrán registrarse proporcionando su correo electrónico y creando una contraseña, o autenticándose a través de una cuenta de Google o GitHub. Consulte el código fuente si desea consultarlo o eche un vistazo mientras comenzamos.
Terminaremos con algo como esto:
configurando
Necesitará una cuenta de Google para usar Firebase, así que obtendrá una si aún no la tiene. Y una vez que lo hagas, podremos patear oficialmente los neumáticos de esta cosa.
En primer lugar, dirígete a Firebase Console y haz clic en la opción “Agregar proyecto” .
A continuación, ingresamos un nombre para el proyecto. Voy con Chatty.
Puede optar por agregar análisis a su proyecto, pero no es obligatorio. De cualquier manera, haga clic en continuar para continuar y Firebase tardará unos segundos en delegar recursos para el proyecto.
Una vez que gira, nos lleva al panel de Firebase. Pero, antes de que podamos comenzar a usar Firebase en nuestra aplicación web, debemos registrar los detalles de configuración de nuestro proyecto. Entonces, haga clic en el ícono web en el tablero.
Luego, ingrese un nombre para la aplicación y haga clic en Registrar aplicación .
A continuación, copiaremos y almacenaremos los detalles de configuración en la siguiente pantalla en un lugar seguro. Eso será útil en el siguiente paso.
Nuevamente, autenticaremos a los usuarios mediante correo electrónico y contraseña, con opciones adicionales para el inicio de sesión única con una cuenta de Google o GitHub. Necesitamos habilitarlos desde la pestaña Autenticación en el panel, pero los revisaremos uno por uno.
Autenticación de correo electrónico y contraseña
Hay una pestaña Método de inicio de sesión en el panel de Firebase. Haga clic en la opción Correo electrónico/Contraseña y habilítela.
¡Ahora podemos usarlo en nuestra aplicación!
Configurar la aplicación web
Para nuestra aplicación web, usaremos React, pero la mayoría de los conceptos se pueden aplicar en cualquier otro marco. Necesitaremos Node.js para configurar React, así que descárgalo e instálalo si aún no lo has hecho.
Usaremos create-react-app para iniciar un nuevo proyecto de React. Esto descarga e instala los paquetes necesarios para una aplicación React. En la terminal, cd
donde desea que vaya nuestro proyecto Chatty y ejecute esto para inicializarlo:
npx create-react-app chatty
Este comando realiza la configuración inicial de nuestra aplicación de reacción e instala las dependencias en package.json. También instalaremos algunos paquetes adicionales. Entonces, entremos cd
en el proyecto en sí y agregamos paquetes para React Router y Firebase.
cd chattyyarn add react-router-dom firebase
Ya sabemos por qué necesitamos Firebase, pero ¿por qué React Router? Nuestra aplicación de chat tendrá un par de vistas. Podemos usar React Router para manejar la navegación entre páginas.
Una vez hecho esto, podemos iniciar oficialmente la aplicación:
yarn start
Esto inicia un servidor de desarrollo y abre una URL en su navegador predeterminado. Si todo se instaló correctamente, deberías ver una pantalla como esta:
Si observa la estructura de carpetas, verá algo similar a esto:
Para nuestra aplicación de chat, esta es la estructura de carpetas que usaremos:
/components
: contiene widgets reutilizables utilizados en diferentes páginas/helpers
: un conjunto de funciones reutilizables/pages
: las vistas de la aplicación/services
: servicios de terceros que utilizamos (por ejemplo, Firebase)App.js
: el componente raíz
Cualquier otra cosa en la carpeta es innecesaria para este proyecto y se puede eliminar de forma segura. Desde aquí, agreguemos algo de código src/services/firebase.js
para que la aplicación pueda comunicarse con Firebase.
import firebase from 'firebase';
Incorporemos Firebase a la aplicación
Importaremos e inicializaremos Firebase usando los detalles de configuración que copiamos anteriormente al registrar la aplicación en el panel de Firebase. Luego, exportaremos los módulos de autenticación y base de datos.
const config = { apiKey: "ADD-YOUR-DETAILS-HERE", authDomain: "ADD-YOUR-DETAILS-HERE", databaseURL: "ADD-YOUR-DETAILS-HERE"};firebase.initializeApp(config);export const auth = firebase.auth;export const db = firebase.database();
Importemos nuestras dependencias en src/App.js
:
import React, { Component } from 'react';import { Route, BrowserRouter as Router, Switch, Redirect,} from "react-router-dom";import Home from './pages/Home';import Chat from './pages/Chat';import Signup from './pages/Signup';import Login from './pages/Login';import { auth } from './services/firebase';
Estas son importaciones de ES6. Específicamente, estamos importando React y otros paquetes necesarios para desarrollar la aplicación. También estamos importando todas las páginas de nuestra aplicación que configuraremos más tarde a nuestro enrutador.
El siguiente paso es el enrutamiento
Nuestra aplicación tiene rutas públicas (accesibles sin autenticación) y una ruta privada (accesible solo con autenticación). Debido a que React no proporciona una forma de verificar el estado autenticado, crearemos componentes de orden superior (HOC) para ambos tipos de rutas.
Nuestros HOC:
- envolver un
Route
, - pasar accesorios del enrutador al
Route
, - renderizar el componente dependiendo del estado autenticado, y
- redirigir al usuario a una ruta específica si no se cumple la condición
Escribamos el código para nuestro PrivateRoute
HOC.
function PrivateRoute({ component: Component, authenticated, ...rest }) { return ( Route {...rest} render={(props) = authenticated === true ? Component {...props} / : Redirect to={{ pathname: '/login', state: { from: props.location } }} /} / )}
Recibe tres accesorios: el componente que se representará si la condición es verdadera, el authenticated
estado y el operador de extensión de ES6 para obtener los parámetros restantes pasados desde el enrutador. Comprueba si authenticated
es verdadero y muestra el componente pasado; de lo contrario, redirige a/iniciar sesión.
function PublicRoute({ component: Component, authenticated, ...rest }) { return ( Route {...rest} render={(props) = authenticated === false ? Component {...props} / : Redirect to='/chat' /} / )}
Es PublicRoute
más o menos lo mismo. Representa nuestras rutas públicas y redirige a la /chat
ruta si el estado autenticado se vuelve verdadero. Podemos usar los HOC en nuestro método de representación:
render() { return this.state.loading === true ? h2Loading.../h2 : ( Router Switch Route exact path="/" component={Home}/Route PrivateRoute path="/chat" authenticated={this.state.authenticated} component={Chat}/PrivateRoute PublicRoute path="/signup" authenticated={this.state.authenticated} component={Signup}/PublicRoute PublicRoute path="/login" authenticated={this.state.authenticated} component={Login}/PublicRoute /Switch /Router );}
Comprobando la autenticación
Sería bueno mostrar un indicador de carga mientras verificamos si el usuario está autenticado. Una vez completada la verificación, representamos la ruta adecuada que coincida con la URL. Contamos con tres rutas públicas — Home
, Login
y— Signup
y una privada llamada Chat
.
Escribamos la lógica para verificar si el usuario está realmente autenticado.
class App extends Component { constructor() { super(); this.state = { authenticated: false, loading: true, }; }}export default App;
Aquí estamos configurando el estado inicial de la aplicación. Luego, usamos el componentDidMount
enlace del ciclo de vida para verificar si el usuario está autenticado. Entonces, agreguemos esto después del constructor:
componentDidMount() { auth().onAuthStateChanged((user) = { if (user) { this.setState({ authenticated: true, loading: false, }); } else { this.setState({ authenticated: false, loading: false, }); } })}
Firebase proporciona un método intuitivo llamado onAuthStateChanged
que se activa cuando cambia el estado autenticado. Usamos esto para actualizar nuestro estado inicial. user
es nulo si el usuario no está autenticado. Si user
es cierto, actualizamos autenticados en true
; de lo contrario lo configuramos en false
. También configuramos la carga en false
cualquier dirección.
Registrar usuarios con correo electrónico y contraseña
Los usuarios podrán registrarse en Chatty mediante correo electrónico y contraseña. La helpers
carpeta contiene un conjunto de métodos que usaremos para manejar cierta lógica de autenticación. Dentro de esta carpeta, creemos un nuevo archivo llamado auth.js
y agreguemos esto:
import { auth } from "../services/firebase";
Importamos el módulo de autenticación del servicio que creamos anteriormente.
export function signup(email, password) { return auth().createUserWithEmailAndPassword(email, password);}
export function signin(email, password) { return auth().signInWithEmailAndPassword(email, password);}
Tenemos dos métodos aquí: signup
y signin
:
signup
creará un nuevo usuario utilizando su correo electrónico y contraseña.signin
iniciará sesión como usuario existente creado con correo electrónico y contraseña.
Creemos nuestra Signup
página creando un nuevo archivo Signup.js
en la carpeta de páginas. Este es el marcado para la interfaz de usuario:
import React, { Component } from 'react';import { Link } from 'react-router-dom';import { signup } from '../helpers/auth';
export default class SignUp extends Component {
render() { return ( div form onSubmit={this.handleSubmit} h1 Sign Up to Link to="/"Chatty/Link /h1 pFill in the form below to create an account./p div input placeholder="Email" name="email" type="email" onChange={this.handleChange} value={this.state.email}/input /div div input placeholder="Password" name="password" onChange={this.handleChange} value={this.state.password} type="password"/input /div div {this.state.error ? p{this.state.error}/p : null} button type="submit"Sign up/button /div hr/hr pAlready have an account? Link to="/login"Login/Link/p /form /div ) }}
Los campos de formulario y de entrada están vinculados a un método que aún no hemos creado, así que solucionémoslo. Justo antes del render()
método, agregaremos lo siguiente:
constructor(props) { super(props); this.state = { error: null, email: '', password: '', }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this);}
Estamos configurando el estado inicial de la página. También vinculamos los métodos handleChange
y handleSubmit
al alcance de este componente.
handleChange(event) { this.setState({ [event.target.name]: event.target.value });}
A continuación, agregaremos el handleChange
método al que están vinculados nuestros campos de entrada. El método utiliza propiedades calculadas para determinar dinámicamente la clave y establecer la variable de estado correspondiente.
async handleSubmit(event) { event.preventDefault(); this.setState({ error: '' }); try { await signup(this.state.email, this.state.password); } catch (error) { this.setState({ error: error.message }); }}
Para handleSubmit, estamos evitando el comportamiento predeterminado para los envíos de formularios (que simplemente recarga el navegador, entre otras cosas). También estamos limpiando la variable de estado de error y luego usamos el método signup() importado de helpers/auth para pasar el correo electrónico y la contraseña ingresados por el usuario.
Si el registro se realiza correctamente, los usuarios son redirigidos a la ruta /Chats. Esto es posible con la combinación de onAuthStateChanged y los HOC que creamos anteriormente. Si el registro falla, configuramos la variable de error que muestra un mensaje a los usuarios.
Autenticar usuarios con correo electrónico y contraseña
La página de inicio de sesión es idéntica a la página de registro. La única diferencia es que usaremos el signin
método de los ayudantes que creamos anteriormente. Dicho esto, creemos otro archivo nuevo en el directorio de páginas, esta vez llamado Login.js
, con este código:
import React, { Component } from "react";import { Link } from "react-router-dom";import { signin, signInWithGoogle, signInWithGitHub } from "../helpers/auth";
export default class Login extends Component { constructor(props) { super(props); this.state = { error: null, email: "", password: "" }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); }
handleChange(event) { this.setState({ [event.target.name]: event.target.value }); }
async handleSubmit(event) { event.preventDefault(); this.setState({ error: "" }); try { await signin(this.state.email, this.state.password); } catch (error) { this.setState({ error: error.message }); } }
render() { return ( div form autoComplete="off" onSubmit={this.handleSubmit} h1 Login to Link to="/" Chatty /Link /h1 p Fill in the form below to login to your account. /p div input placeholder="Email" name="email" type="email" onChange={this.handleChange} value={this.state.email} / /div div input placeholder="Password" name="password" onChange={this.handleChange} value={this.state.password} type="password" / /div div {this.state.error ? ( p{this.state.error}/p ) : null} button type="submit"Login/button /div hr / p Don't have an account? Link to="/signup"Sign up/Link /p /form /div ); }}
De nuevo, muy similar a antes. Cuando el usuario inicia sesión correctamente, se le redirige a /chat
.
Autenticarse con una cuenta de Google
Firebase nos permite autenticar a los usuarios con una cuenta de Google válida. Tenemos que habilitarlo en el panel de Firebase tal como lo hicimos para el correo electrónico y la contraseña.
En esa misma página, también debemos desplazarnos hacia abajo para agregar un dominio a la lista de dominios autorizados para acceder a la función. De esta forma evitamos el spam de cualquier dominio que no esté en la lista blanca. Para fines de desarrollo, nuestro dominio es localhost
, así que usaremos ese por ahora.
Podemos volver a nuestro editor ahora. Agregaremos un nuevo método para helpers/auth.js
manejar la autenticación de Google.
export function signInWithGoogle() { const provider = new auth.GoogleAuthProvider(); return auth().signInWithPopup(provider);}
Aquí, estamos creando una instancia de GoogleAuthProvider
. Luego, llamamos signInWithPopup
con el proveedor como parámetro. Cuando se llama a este método, aparecerá una ventana emergente que guiará al usuario a través del flujo de inicio de sesión de Google antes de redirigirlo nuevamente a la aplicación. Probablemente lo hayas experimentado tú mismo en algún momento.
Usémoslo en nuestra página de registro importando el método:
import { signin, signInWithGoogle } from "../helpers/auth";
Luego, agreguemos un botón para activar el método, justo debajo del botón Registrarse :
pOr/pbutton onClick={this.googleSignIn} type="button" Sign up with Google/button
A continuación, agregaremos el onClick
controlador:
async googleSignIn() { try { await signInWithGoogle(); } catch (error) { this.setState({ error: error.message }); }}
Ah, y debemos recordar vincular el controlador al componente:
constructor() { // ... this.githubSignIn = this.githubSignIn.bind(this);}
¡Eso es todo lo que necesitamos! Cuando se hace clic en el botón, lleva a los usuarios a través del flujo de inicio de sesión de Google y, si tiene éxito, la aplicación redirige al usuario a la ruta de chat.
Autenticarse con una cuenta de GitHub
Vamos a hacer lo mismo con GitHub. También puede brindarles a las personas más de una opción de cuenta.
Sigamos los pasos. Primero, habilitaremos el inicio de sesión de GitHub en el panel de Firebase, como lo hicimos con el correo electrónico y Google.
Notará que tanto el campo ID del cliente como el campo secreto del cliente están vacíos, pero tenemos nuestra URL de devolución de llamada de autorización en la parte inferior. Cópielo, porque lo usaremos cuando hagamos lo siguiente, que es registrar la aplicación en GitHub.
Una vez hecho esto, obtendremos un ID de cliente y un secreto que ahora podemos agregar a Firebase console.
Volvamos al editor y agreguemos un nuevo método a helpers/auth.js
:
export function signInWithGitHub() { const provider = new auth.GithubAuthProvider(); return auth().signInWithPopup(provider);}
Es similar a la interfaz de inicio de sesión de Google, pero esta vez estamos creando un archivo GithubAuthProvider
. Luego, llamaremos signInWithPopup
con el proveedor.
En pages/Signup.js
, actualizamos nuestras importaciones para incluir el signInWithGitHub
método:
import { signup, signInWithGoogle, signInWithGitHub } from "../helpers/auth";
Agregamos un botón para registrarse en GitHub:
button type="button" onClick={this.githubSignIn} Sign up with GitHub/button
Luego agregamos un controlador de clic para el botón que activa el flujo de registro de GitHub:
async githubSignIn() { try { await signInWithGitHub(); } catch (error) { this.setState({ error: error.message }); }}
Recordemos nuevamente vincular el controlador al componente:
constructor() { // ... this.githubSignIn = this.githubSignIn.bind(this);}
Ahora obtendremos el mismo flujo de inicio de sesión y autenticación que tenemos con Google, pero con GitHub.
Leyendo datos de Firebase
Firebase tiene dos tipos de bases de datos: un producto al que llaman Realtime Database y otro llamado Cloud Firestore. Ambas bases de datos son bases de datos similares a NoSQL, lo que significa que la base de datos está estructurada como pares clave-valor. Para este tutorial, usaremos la base de datos en tiempo real.
Esta es la estructura que usaremos para nuestra aplicación. Tenemos un nodo raíz chats
con nodos secundarios. Cada niño tiene un contenido, una marca de tiempo y una identificación de usuario. Una de las pestañas que notará es Reglas, que es cómo configuramos los permisos sobre el contenido de la base de datos.
Las reglas de la base de datos de Firebase también se definen como pares clave-valor. Aquí, estableceremos nuestras reglas para permitir que solo los usuarios autenticados lean y escriban en el nodo de chat. Hay muchas más reglas de base de fuego. vale la pena echarle un vistazo.
Escribamos código para leer de la base de datos. Primero, cree un nuevo archivo llamado Chat.js
en la carpeta de páginas y agregue este código para importar React, autenticación de Firebase y Realtime Database:
import React, { Component } from "react";import { auth } from "../services/firebase";import { db } from "../services/firebase"
A continuación, definamos el estado inicial de la aplicación:
export default class Chat extends Component { constructor(props) { super(props); this.state = { user: auth().currentUser, chats: [], content: '', readError: null, writeError: null }; } async componentDidMount() { this.setState({ readError: null }); try { db.ref("chats").on("value", snapshot = { let chats = []; snapshot.forEach((snap) = { chats.push(snap.val()); }); this.setState({ chats }); }); } catch (error) { this.setState({ readError: error.message }); } }}
La verdadera lógica principal tiene lugar en componentDidMount. db.ref("chats")
una referencia a la ruta de los chats en la base de datos. Escuchamos el evento de valor que se activa cada vez que se agrega un nuevo valor al nodo de chats. Lo que se devuelve de la base de datos es un objeto similar a una matriz que recorremos e insertamos cada objeto en una matriz. Luego, configuramos la variable de estado de los chats en nuestra matriz resultante. Si hay un error, configuramos la readError
variable de estado en el mensaje de error.
Una cosa a tener en cuenta aquí es que se crea una conexión entre el cliente y nuestra base de datos de Firebase porque usamos el .on()
método. Esto significa que cada vez que se agrega un nuevo valor a la base de datos, la aplicación cliente se actualiza en tiempo real, lo que significa que los usuarios pueden ver nuevos chats sin necesidad de actualizar la página. ¡Bien!.
Después componentDidMount
, podemos renderizar nuestros chats así:
render() { return ( div div className="chats" {this.state.chats.map(chat = { return p key={chat.timestamp}{chat.content}/p })} /div div Login in as: strong{this.state.user.email}/strong /div /div );}
Esto representa la variedad de chats. Representamos el correo electrónico del usuario actualmente conectado.
Escribir datos en Firebase
Por el momento, los usuarios sólo pueden leer de la base de datos pero no pueden enviar mensajes. Lo que necesitamos es un formulario con un campo de entrada que acepte un mensaje y un botón para enviar el mensaje al chat.
Entonces, modifiquemos el marcado así:
return ( div div className="chats" {this.state.chats.map(chat = { return p key={chat.timestamp}{chat.content}/p })} /div {# message form #} form onSubmit={this.handleSubmit} input onChange={this.handleChange} value={this.state.content}/input {this.state.error ? p{this.state.writeError}/p : null} button type="submit"Send/button /form div Login in as: strong{this.state.user.email}/strong /div /div );}
Hemos agregado un formulario con un campo de entrada y un botón. El valor del campo de entrada está vinculado a nuestra variable de estado content
y lo llamamos handleChange
cuando cambia su valor.
handleChange(event) { this.setState({ content: event.target.value });}
handleChange
obtiene el valor del campo de entrada y lo establece en nuestra variable de estado. Para enviar el formulario, llamamos a handleSubmit
:
async handleSubmit(event) { event.preventDefault(); this.setState({ writeError: null }); try { await db.ref("chats").push({ content: this.state.content, timestamp: Date.now(), uid: this.state.user.uid }); this.setState({ content: '' }); } catch (error) { this.setState({ writeError: error.message }); }}
Configuramos los errores anteriores en null
. Creamos una referencia al chats
nodo en la base de datos y la usamos push()
para crear una clave única y enviarle el objeto.
Como siempre, tenemos que vincular nuestros métodos al componente:
constructor(props) { // ... this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this);}
¡Ahora un usuario puede agregar nuevos mensajes a los chats y verlos en tiempo real! ¿Cuan genial es eso?
¡Hora de la demostración!
¡Disfruta de tu nueva aplicación de chat!
¡Felicidades! Acaba de crear una herramienta de chat que autentica a los usuarios con correo electrónico y contraseña, además de opciones para autenticarse a través de una cuenta de Google o GitHub.
Espero que esto le dé una buena idea de lo útil que puede ser Firebase para poner en marcha la autenticación en una aplicación. Trabajamos en una aplicación de chat, pero la verdadera joya son los métodos de registro e inicio de sesión que creamos para acceder a ella. Eso es algo útil para muchas aplicaciones.
¿Preguntas? ¿Pensamientos? ¿Comentario? ¡Házmelo saber en los comentarios!
Deja un comentario