En esta ocasión te mostrare como usar el servicio de Firebase para realizar una autenticación en tu aplicación de React JS.
Cualquier tipo de feedback es bienvenido, gracias y espero disfrutes el articulo.🤗
⚠️ Nota: Es necesario que cuentes con conocimientos básicos en React JS y hooks y TypeScript.
Tabla de contenido
📌 Tecnologías a utilizar.
📌 Creando el proyecto.
📌 Primeros pasos.
📌 Creando los diseños de autenticación.📌 Manejando los estados de los formularios.
📌 Configurar Firebase.📌 Creando el proyecto.
📌 Creando la app.
📌 Configurando la autenticación.📌 Configurando Firebase en nuestra aplicación de React.
📌 Creando las funciones para la autenticación.📌 1 - Creando la función para autenticarse por Google.
📌 2 - Creando la función para autenticarse por credenciales.
📌 3 - Creando la función para observar los cambios en el estado de autenticación del usuario.
📌 4 - Creando la función para cerrar sesión.📌 Creando un contexto de nuestra aplicación.
📌 Usando nuestro contexto.
📌 Conclusión.
🔥 Tecnologías a utilizar.
- ▶️ React JS (version 18)
- ▶️ Vite JS
- ▶️ TypeScript
- ▶️ Firebase (version 9.10)
- ▶️ CSS vanilla (Los estilos los encuentras en el repositorio al final de este post)
🔥 Creando el proyecto.
Al proyecto le colocaremos el nombre de: auth-firebase-react
(opcional, tu le puedes poner el nombre que gustes).
npm init vite@latest
Creamos el proyecto con Vite JS y seleccionamos React con TypeScript.
Luego ejecutamos el siguiente comando para navegar al directorio que se acaba de crear.
cd auth-firebase-react
Luego instalamos las dependencias.
npm install
Después abrimos el proyecto en un editor de código (en mi caso VS code).
code .
🔥 Primeros pasos.
Primero vamos al archivo src/App.tsx
y borramos todo, vamos a crear un nuevo componente que muestre un simple titulo.
const App = () => {
return (
<main>
<h1><b>Auth with</b> <span>Firebase</span> <b>and</b> <span>React</span></h1>
</main>
)
}
export default App
Se vería asi, si es que usas los mismos estilos que este proyecto. 👀
🔥 Creando los diseños de autenticación.
En esta aplicación no manejaremos rutas, solamente nos enfocamos en la autenticación. Asi que los diseños de login y register pueden ponerlos en vistas separadas si gustan, en mi caso solo serán componentes.
Lo hago de esta manera para explicarlo de la manera más simple.
🔥 Diseño del inicio de sesión.
Creamos la carpeta src/components
y dentro creamos el archivo Login.tsx
El inicio de sesión constara de 2 inputs:
- Email.
- Password.
Los input, deben de tener contener el atributo name identificando cada input.
También tendrá 2 botones:
- Inicio de sesión normal.
- Inicio de sesión con Google.
Cada botón debe tener su propiedad type esto es para que el botón de Google no haga el posteo del formulario, solamente el primer botón debe hacer eso.
export const Login = () => {
return (
<div className="container-auth">
<h2>Login</h2>
<form>
<input
name="email"
type="email"
placeholder="E-mail"
/>
<input
name="pass"
type="password"
placeholder="Password"
/>
<div className="container-buttons">
<button type="submit">Log In</button>
<button type="button"> Google </button>
</div>
</form>
</div>
)
}
🔥 Diseño del registro.
El diseño del registro sera igual al del inicio de sesión, con los mismos inputs pero solamente tiene un botón
export const Register = () => {
return (
<div className="container-auth">
<h2>Create an account</h2>
<form>
<input
name="email"
type="email"
placeholder="E-mail"
/>
<input
name="pass"
type="password"
placeholder="Password"
/>
<div className="container-buttons">
<button type="submit">Sign up</button>
</div>
</form>
</div>
)
}
Ahora creamos un archivo barril, (index.ts) en la carpeta src/components
para poder exportar nuestros componentes e importarlos en otro lugar de una manera mas ordenada.
export * from './Login'
export * from './Register'
Una vez tengamos los dos diseños y el archivo barril, vamos a src/App.tsx
y los agregamos:
import { Login, Register } from "./components"
const App = () => {
return (
<main>
<h1><b>Auth with</b> <span>Firebase</span> <b>and</b> <span>React</span></h1>
<section>
<Login />
<Register />
</section>
</main>
)
}
export default App
El resultado del diseño seria el siguiente:
🔥 Manejando los estados de los formularios.
Para manejar cada formulario, vamos a implementar un custom hook, lo llamaremos useForm.tsx y estará dentro de la carpeta src/hooks
.
Creamos una función que recibe como parámetro un objeto que contenga el estado inicial del formulario y este tendrá un tipo genérico, esto para que sea un poco mas reutilizable (por si quieren agregar mas campos a los formularios
) el hook aunque en este caso no es necesario ya que tenemos los mismos campos en ambos formularios
interface Props<T> {
initialState: T
}
export const useForm = <T>({ initialState }: Props<T>) => {}
Después, usaremos el estado para almacenar los valores del formulario. y con la función handleChange vamos a poder manejar los cambios del input y almacenar sus valores.
La función handleChange mandamos una propiedad computada a la función setForm y es por eso que es necesario el atributo name en el input, para identificarlo y obtener su valor.
Finalmente retornamos, mediante el operador rest esparcimos los valores del formulario, luego el formulario y finalmente la función handleChange.
import { useState } from 'react';
type eventInput = React.ChangeEvent<HTMLInputElement>;
interface Props<T> {
initialState: T
}
export const useForm = <T>({ initialState }: Props<T>) => {
const [form, setForm] = useState<T>(initialState)
const handleChange = (e: eventInput) => {
setForm(prev => ({
...prev,
[e.target.name]: e.target.value
}))
}
return { ...form, form, handleChange }
}
Ahora usamos el hook en src/components/Login.tsx
:
Llamamos al hook useForm le mandamos un objeto que sera el estado inicial del formulario, en este caso tendremos dos propiedades que es el email y pass, que hacen referencia a los inputs que tenemos en el HTML. Sus valores por defecto son string vacíos.
Desestructuramos las propiedades del formulario y la función handleChange.
const { handleChange, pass, email } = useForm({
initialState: {
email: '',
pass: ''
}
})
En los inputs colocamos el atributo value con su correspondiente valor y el atributo onChange mandando la función handleChange para manejar el estado del input.
<input
name="email"
type="email"
placeholder="E-mail"
onChange={handleChange}
value={email}
/>
<input
name="pass"
type="password"
placeholder="Password"
onChange={handleChange}
value={pass}
/>
Finalmente, haremos una función llamada handleSubmit que recibe el evento del formulario, y esta función por el momento solo previene el comportamiento del formulario por defecto.
La función handleSubmit se la pasamos al atributo onSubmit de la etiqueta form.
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
}
Asi quedaría por el momento el Login.tsx
import { useForm } from '../hooks/useForm';
export const Login = () => {
const { handleChange, pass, email } = useForm({
initialState: {
email: '',
pass: ''
}
})
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
}
return (
<div className="container-auth">
<h2>Login</h2>
<form onSubmit={handleSubmit}>
<input
name="email"
type="email"
placeholder="E-mail"
onChange={handleChange}
value={email}
/>
<input
name="pass"
type="password"
placeholder="Password"
onChange={handleChange}
value={pass}
/>
<div className="container-buttons">
<button type="submit">Log In</button>
<button type="button"> Google </button>
</div>
</form>
</div>
)
}
Asi como hicimos el Login.tsx es exactamente igual en el archivo Register.tsx
import { useForm } from "../hooks/useForm"
export const Register = () => {
const { handleChange, pass, email } = useForm({
initialState: {
email: '',
pass: ''
}
})
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
}
return (
<div className="container-auth">
<h2>Create an account</h2>
<form onSubmit={handleSubmit}>
<input
name="email"
type="email"
placeholder="E-mail"
onChange={handleChange}
value={email}
/>
<input
name="pass"
type="password"
placeholder="Password"
onChange={handleChange}
value={pass}
/>
<div className="container-buttons">
<button type="submit">Sign up</button>
</div>
</form>
</div>
)
}
⚠️ Nota: Incluso pueden crear un único componente reutilizable, ya que los formularios son casi exactamente iguales solo se diferencian por los botones y la acción de handleSubmit ya que esta función hará algo diferente dependiendo el formulario.
Ya tenemos el diseño y funcionalidad de los formularios, seguimos con la creación de nuestra app en Firebase.
🔥 Configurar Firebase.
Ahora nos toca configurar la aplicación en firebase para poder usar su servicio de autenticación.
🔥 Creando el proyecto.
1 - Vamos a la consola de firebase, iniciamos sesión con alguna cuenta de correo de Gmail.
2 - Si es la primera vez que usan Firebase, les aparecerá un mensaje y botón de crear proyecto el cual deben de presionar.
3 - Colocar el nuevo nombre al proyecto. En mi caso le puse auth-firebase-react.
Y al final hay un botón de continuar que debes presionar.
4 - Esperar a que tu proyecto termine de crearse y después dar click en continuar
Una vez en continuar te va a mandar a un nuevo panel.
🔥 Creando el la app.
1 - En el nuevo panel, tienes que identificar estos botones. Presiona el botón de web para crear una app (el tercer botón con fondo blanco).
2 - Coloca el nombre a tu app y dale click en Registrar app
3 - Una vez registrada la app, nos darán nuestras credenciales, las cuales tenemos que guardar porque las vamos a usar después.
🔥 Configurando la autenticación.
1 - Ahora, tenemos que regresar al panel, en el menu del lado derecho hay una opción que dice compilación y dentro esta la opción de autenticación y tienes que darle click, para que te lleve a otra pantalla.
Y tienes que dar click al botón de Comenzar
2 - Después se te mostraran diversos proveedores, de los cuales vamos a seleccionar el de correo electrónico/contraseña y Google (puedes elegir los que quieras, aunque probablemente tendrás que hacer mas configuración).
Cuando elijas el de correo electrónico/contraseña solamente le das en Habilitar y al final viene un botón de guardar que debes presionar una vez que termines de hacer alguna modificación en el proveedor.
Cuando elijas el de Google harás lo mismo que el anterior, y también tendrás que seleccionar tu correo electrónico.
3 - Una vez habilitados los proveedores, tendrá que aparecerte en la pestaña Sign-in method de la siguiente forma.
4 - En la pestaña Users puedes ver todos los usuarios que se registren en tu aplicación.
🔥 Configurando Firebase en nuestra aplicación de React.
De vuelta a nuestra app de React, vamos a instalar Firebase.
npm install firebase
creamos una nueva carpeta src/firebase
y dentro un archivo llamado config.ts y pegamos toda la configuración que nos dieron en la sección anterior
En mi caso, yo coloque los valores de cada propiedad en una variable de entorno, solamente creando en la raíz del proyecto un archivo .env.
Cada variable debe empezar con la palabra VITE_ para que funcionen.
VITE_APIKEY=1231231465
# more vars
Y para llamar a una variable de entorno tenemos que user el import.meta.env['Nombre de la variable']
Nota: también debes notar que cambie el nombre de la variable app por FirebaseApp
import { initializeApp } from "firebase/app";
const firebaseConfig = {
apiKey: import.meta.env.VITE_APIKEY,
authDomain: import.meta.env.VITE_AUTHDOMAIN,
projectId: import.meta.env.VITE_PROJECTID,
storageBucket: import.meta.env.VITE_STORAGEBUCKET,
messagingSenderId: import.meta.env.VITE_MESSAGINGSENDERID,
appId: import.meta.env.VITE_APPID,
};
// Initialize Firebase
export const FirebaseApp = initializeApp(firebaseConfig)
Ahora para utilizar el servicio de autenticación de Firebase, usamos el método getAuth y tenemos que obtenerlo de 'firebase/auth', después le mandamos la inicialización de nuestra app, o sea la constante FirebaseApp
import { getAuth } from 'firebase/auth'
export const FirebaseApp = initializeApp(firebaseConfig)
export const FirebaseAuth = getAuth(FirebaseApp)
La configuración quedaría de esta manera 👀:
import { initializeApp } from "firebase/app";
const firebaseConfig = {
apiKey: import.meta.env.VITE_APIKEY,
authDomain: import.meta.env.VITE_AUTHDOMAIN,
projectId: import.meta.env.VITE_PROJECTID,
storageBucket: import.meta.env.VITE_STORAGEBUCKET,
messagingSenderId: import.meta.env.VITE_MESSAGINGSENDERID,
appId: import.meta.env.VITE_APPID,
};
export const FirebaseApp = initializeApp(firebaseConfig)
export const FirebaseAuth = getAuth(FirebaseApp)
🔥 Creando las funciones para la autenticación.
Ahora vamos a crear un nuevo archivo dentro de src/firebase
llamado services.ts
Nota: todas las funciones de firebase que vamos a usar vienen de firebase/auth
🔥 1 - Creando la función para autenticarse por Google.
Primero debemos crear una nueva instancia del proveedor que hayamos escogido, en este caso Google.
Luego creamos un método asíncrono, y dentro un try/catch porque ya sea que el usuario se equivoque o algo salga mal.
Mediante el método signInWithPopup, tendemos que mandarle nuestra instancia de FirebaseAuth, que ya habíamos creado en la sección anterior, y la instancia del proveedor.
Si todo sale correcto, de la propiedad user de la variable resultado, te dará varia información como lo puedes ver en la desestructuración, pero solo vamos a usar el uid por eso lo retornamos.
Y en el catch, de hecho en todos los catch de este archivo, solo vamos a mandar una alerta con el mensaje que nos proporciona Firebase
import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth'
import { FirebaseAuth } from './config'
const googleProvider = new GoogleAuthProvider()
export const singInWithGoogle = async () => {
try {
const result = await signInWithPopup(FirebaseAuth, googleProvider)
const { displayName, email, photoURL, uid } = result.user
return uid
} catch (e) {
alert((e as Error).message)
}
}
🔥 2 - Creando la función para autenticarse por credenciales.
La función para login y register usando credenciales son las mismas solo se diferencian por el método.
Ambas reciben un objeto que contiene el email y password y si todo sale bien, retornan el uid (estas funciones también devuelven lo mismo que el de autenticarse con google, como displayName, photoURL, etc.)
Tanto la función de createUserWithEmailAndPassword y signInWithEmailAndPassword reciben la instancia de FirebaseAuth, y un email y password.
- createUserWithEmailAndPassword, crea el usuario en Firebase.
- signInWithEmailAndPassword, verifica si existe el usuario en Firebase.
import { createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth'
import { FirebaseAuth } from './config'
interface PropsRegister { email: string, password: string }
export const signInWithCredentials = async ({ email, password }: PropsRegister) => {
try {
const resp = await createUserWithEmailAndPassword(FirebaseAuth, email, password);
return resp.user.uid
} catch (e) {
alert((e as Error).message)
}
}
export const loginWithCredentials = async ({ email, password }: PropsRegister) => {
try {
const resp = await signInWithEmailAndPassword(FirebaseAuth, email, password);
return resp.user.uid
} catch (e) {
alert((e as Error).message)
}
}
🔥 3 - Creando la función para observar los cambios en el estado de autenticación del usuario.
¿Por que queremos observar el estado de autenticación del usuario?
Bueno, supongamos que iniciamos sesión correctamente, todo sale muy bien, estamos dentro de la application ya autenticados 🤩. Pero upss! refrescamos el navegador y se nos pierde la sesión, y tenemos que volver a iniciar sesión 😥.
Asi que como resolvemos este problema, pues observando el estado de autenticación del usuario.
Para ello necesitamos un par de cosas.
Primero crear una función, que va a recibir como parámetro un callback, o sea una función, dicha función nos ayudara a establecer el usuario autenticado o no autenticado.
Puedes notar en el código que usaremos un setter de useState y que ademas usaremos el Context API.El tipado les fallara porque aun no tenemos creado el context, asi que por el momento pueden colocar el tipo any.
Pero lo importante ahora es que recibimos la función setSession.
// type StateDispatch = React.Dispatch<React.SetStateAction<Pick<AuthStateContext, "status" | "userId">>>
type StateDispatch = any
export const onAuthStateHasChanged = (setSession: StateDispatch) => {}
Ahora usaremos la función onAuthStateChanged, que recibe como primer parámetro el FirebaseAuth
import { onAuthStateChanged } from 'firebase/auth'
import { FirebaseAuth } from './config'
// type StateDispatch = React.Dispatch<React.SetStateAction<Pick<AuthStateContext, "status" | "userId">>>
type StateDispatch = any
export const onAuthStateHasChanged = (setSession: StateDispatch) => {
onAuthStateChanged(FirebaseAuth)
}
El segundo parámetro es un callback, que retorna el usuario si es que existe su sesión activa, de lo contrario retorna undefined.
import { onAuthStateChanged } from 'firebase/auth'
import { FirebaseAuth } from './config'
// type StateDispatch = React.Dispatch<React.SetStateAction<Pick<AuthStateContext, "status" | "userId">>>
type StateDispatch = any
export const onAuthStateHasChanged = (setSession: StateDispatch) => {
onAuthStateChanged(FirebaseAuth, user => {
})
}
Evaluamos el usuario:
Si no existe, usamos el setSession para establecer el status en no-authenticated y el id del usuario en null. (no olviden colocar el return para evitar que se ejecute la siguiente linea)
Si existe, usamos el setSession para establecer el status en authenticated y el id del usuario.
import { onAuthStateChanged } from 'firebase/auth'
import { FirebaseAuth } from './config'
// type StateDispatch = React.Dispatch<React.SetStateAction<Pick<AuthStateContext, "status" | "userId">>>
type StateDispatch = any
export const onAuthStateHasChanged = (setSession: StateDispatch) => {
onAuthStateChanged(FirebaseAuth, user => {
if (!user) return setSession({ status: 'no-authenticated', userId: null })
setSession({ status: 'authenticated', userId: user!.uid })
})
}
Probablemente no entiendas porque mandamos status o userId, bueno eso son los datos que necesitaremos en nuestro estado global, cuando vayamos a crear el contexto de nuestra app.
🔥 4 - Creando la función para cerrar sesión.
Ahora que pasa, gracias a que estamos observando el estado de autenticación del usuario, no podemos cambiar de usuario por un buen rato, ni aunque recargues o cierres el navegador.
Bueno para ello, debemos cerrar sesión, y e muy sencillo:
import { FirebaseAuth } from './config'
export const logoutFirebase = async () => await FirebaseAuth.signOut()
🔥 Creando un contexto de nuestra aplicación.
Para crear un contexto primero vamos a crear una carpeta en src/context y dentro un archivo nombrado authContext.tsx
Dentro vamos a definir nuestra interfaz de las propiedades que vamos a compartir en nuestro contexto.
export interface AuthStateContext {
userId: string | null
status: 'checking' | 'authenticated' | 'no-authenticated'
handleLoginWithGoogle: () => Promise<void>
handleLoginWithCredentials: (password: string, email: string) => Promise<void>
handleRegisterWithCredentials: (password: string, email: string) => Promise<void>
handleLogOut: () => Promise<void>
}
Luego vamos a crear el estado inicial de nuestro contexto.
Por defecto el status estará en checking por que al principio no sabemos si esta autenticado o no, lo sabremos una vez que se ejecuten ciertas funciones. Y también el userId sera nulo por defecto hasta comprobar el estado de autenticación del usuario.
const initialState: Pick<AuthStateContext, 'status' | 'userId'> = {
status: 'checking',
userId: null
}
Creamos el contexto.
export const AuthContext = createContext({} as AuthStateContext)
Creamos el proveedor que sera un componente funcional que recibirá el children
interface IElement { children: JSX.Element | JSX.Element[] }
export const AuthProvider = ({ children }: IElement) => {}
Dentro del AuthProvider usaremos un estado y lo inicializamos con el objeto que establecimos con anterioridad.
export const AuthProvider = ({ children }: IElement) => {
const [session, setSession] = useState(initialState)
}
Ahora vamos a usar la función que creamos antes para observar el estado de autenticación del usuario.
Lo haremos en un efecto que solo se debe ejecutar la primera vez que inicie la aplicación. Y el callback que le mandaremos sera el setSession.
const [session, setSession] = useState(initialState)
useEffect(() => {
onAuthStateHasChanged(setSession)
}, [])
Luego haremos la función que ejecuta el cierre de sesión. Llamamos a la función logoutFirebase y establecemos la session con el userId en nulo y el status en no-authenticated
import { logoutFirebase } from '../firebase/providers'
const handleLogOut = async () => {
logoutFirebase()
setSession({ userId: null, status: 'no-authenticated' })
}
Después, haremos una función que reutilizaremos en otras funciones, ya que queremos evitar repetir tanto código.
Esta función recibe el userId que puede ser un string o undefined, evaluamos si el userId es un string:
Si el userId es un string, significa que el usuario esta autenticado y establecemos la sesión con el userId y el status en authenticated. (no olviden colocar el return para evitar que se ejecute la siguiente linea)
Si el userId es undefined, llamamos a la función handleLogOut, ya que el usuario no tiene una autenticación valida, y necesitamos cerrar todas las sesiones.
const validateAuth = (userId: string | undefined) => {
if (userId) return setSession({ userId, status: 'authenticated' })
handleLogOut()
}
Otra función que también vamos a reutilizar es la siguiente, es para establecer el status en checking mientras se realizar alguna validación.
const checking = () => setSession(prev => ({ ...prev, status: 'checking' }))
Las siguientes funciones, asi como esta serán parecidas, primero hacer el checking, luego ejecutar el función especifico para esta tarea el cual nos retorna el userId o undefined y llamar el validateAuth mandando lo que retorna dicha función
1 - Función para iniciar sesión con Google.
import { singInWithGoogle } from '../firebase/providers'
const handleLoginWithGoogle = async () => {
checking()
const userId = await singInWithGoogle()
validateAuth(userId)
}
2 - Función para iniciar sesión con credenciales.
const handleLoginWithCredentials = async (password: string, email: string) => {
checking()
const userId = await loginWithCredentials({ email, password })
validateAuth(userId)
}
3 - Función para crear cuenta con credenciales.
const handleRegisterWithCredentials = async (password: string, email: string) => {
checking()
const userId = await signInWithCredentials({ email, password })
validateAuth(userId)
}
Finalmente, como es un componente funcional, debemos regresar un componente de la siguiente manera, colocando el children.
return (
<AuthContext.Provider>
{children}
</AuthContext.Provider>
)
La etiqueta AuthContext.Provider recibe un value, que son las siguientes propiedades
return (
<AuthContext.Provider
value={{
...session,
handleLoginWithGoogle,
handleLoginWithCredentials,
handleRegisterWithCredentials,
handleLogOut
}}
>
{children}
</AuthContext.Provider>
)
Y terminamos con nuestro contexto. Se vería asi 👀
import { createContext, useEffect, useState } from 'react'
import { loginWithCredentials, logoutFirebase, onAuthStateHasChanged, signInWithCredentials, singInWithGoogle } from '../firebase/providers'
export interface AuthStateContext {
userId: string | null
status: 'checking' | 'authenticated' | 'no-authenticated'
handleLoginWithGoogle: () => Promise<void>
handleLoginWithCredentials: (password: string, email: string) => Promise<void>
handleRegisterWithCredentials: (password: string, email: string) => Promise<void>
handleLogOut: () => Promise<void>
}
const initialState: Pick<AuthStateContext, 'status' | 'userId'> = {
userId: null,
status: 'checking'
}
export const AuthContext = createContext({} as AuthStateContext)
interface IElement { children: JSX.Element | JSX.Element[] }
export const AuthProvider = ({ children }: IElement) => {
const [session, setSession] = useState(initialState)
useEffect(() => {
onAuthStateHasChanged(setSession)
}, [])
const handleLogOut = async () => {
logoutFirebase()
setSession({ userId: null, status: 'no-authenticated' })
}
const validateAuth = (userId: string | undefined) => {
if (userId) return setSession({ userId, status: 'authenticated' })
handleLogOut()
}
const checking = () => setSession(prev => ({ ...prev, status: 'checking' }))
const handleLoginWithGoogle = async () => {
checking()
const userId = await singInWithGoogle()
validateAuth(userId)
}
const handleLoginWithCredentials = async (password: string, email: string) => {
checking()
const userId = await loginWithCredentials({ email, password })
validateAuth(userId)
}
const handleRegisterWithCredentials = async (password: string, email: string) => {
checking()
const userId = await signInWithCredentials({ email, password })
validateAuth(userId)
}
return (
<AuthContext.Provider
value={{
...session,
handleLoginWithGoogle,
handleLoginWithCredentials,
handleRegisterWithCredentials,
handleLogOut
}}
>
{children}
</AuthContext.Provider>
)
}
Ahora necesitamos envolver nuestra app con el proveedor. Para ello vamos al punto mas alto de nuestra app que es el archivo src/main.tsx
y agregamos el AuthProvider
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { AuthProvider } from './context/authContext'
import './index.css'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<AuthProvider>
<App />
</AuthProvider>
</React.StrictMode>
)
🔥 Usando nuestro contexto.
Ahora nos vamos a el archivo src/components/Register.tsx
y usamos nuestro contexto de la siguiente manera:
Importamos el hook useContext y le mandamos el AuthContext y obtenemos la función handleRegisterWithCredentials
Dicha función la ejecutamos dentro de handleSubmit y le mandamos el email y password.
import { useContext } from 'react';
import { AuthContext } from "../context/authContext";
export const Register = () => {
const { handleRegisterWithCredentials } = useContext(AuthContext)
const { handleChange, pass, email } = useForm({
initialState: {
email: 'test@test2.com',
pass: '123456'
}
})
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
handleRegisterWithCredentials(pass, email)
}
// ...
Lo mismo hacemos en src/components/Login.tsx
.
import { useContext } from 'react';
import { AuthContext } from '../context/authContext';
export const Login = () => {
const { handleLoginWithCredentials } = useContext(AuthContext)
const { handleChange, pass, email } = useForm({
initialState: {
email: 'test@test1.com',
pass: '123456'
}
})
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
handleLoginWithCredentials(pass, email)
}
// ...
Pero también necesitamos la función handleLoginWithGoogle
const { handleLoginWithGoogle, handleLoginWithCredentials } = useContext(AuthContext)
Dicha función se va a ejecutar el atributo onClick de la etiqueta button de Google.
<button type="button" onClick={handleLoginWithGoogle}> Google </button>
Finalmente en nuestro archivo src/App.tsx
Vamos a usar el contexto extrayendo el status y el userID.
Evaluamos el status y si es checking, mostramos un loading.
const { status, userId } = useContext(AuthContext)
if (status === 'checking') return <p className="loading"><span>Checking credentials, wait a moment...</span></p>
Ahora al final del archivo crearemos dos componentes.
El primero es el HomePage (para simular que es un pagina diferente).
Este componente solo sera visible cuando el usuario este autenticado.
Y se mostrara el userID y un botón que ejecuta el cerrar session.
export const HomePage = () => {
const { userId, handleLogOut } = useContext(AuthContext)
return (
<section>
<h5>Your ID is: <span>{userId}</span></h5>
<button className="btn-logout" onClick={handleLogOut}>Log out</button>
</section>
)
}
El segundo componente es el AuthPage, (simula otra pagina diferente).
Este componente solo sera visible cuando el usuario NO este autenticado.
Solo se muestran los componentes Login y Register que teníamos en nuestro componente App.
export const AuthPage = () => {
return (
<section>
<Login />
<Register />
</section>
)
}
Ahora en el componente App, vamos hacer una validación. Donde si el status, es authenticated y existe el userId, mostramos el HomePage, de lo contrario que muestre el AuthPage
return (
<main>
<h1><b>Auth with</b> <span>Firebase</span> <b>and</b> <span>React</span></h1>
{
(status === 'authenticated' && userId)
? <HomePage />
: <AuthPage />
}
</main>
)
El archivo App.tsx quedaría de la siguiente manera 👀:
import { useContext } from "react"
import { Login, Register } from "./components"
import { AuthContext } from './context/authContext';
const App = () => {
const { status, userId } = useContext(AuthContext)
if (status === 'checking') return <p className="loading"><span>Checking credentials, wait a moment...</span></p>
return (
<main>
<h1><b>Auth with</b> <span>Firebase</span> <b>and</b> <span>React</span></h1>
{
(status === 'authenticated' && userId)
? <HomePage />
: <AuthPage />
}
</main>
)
}
export default App
export const HomePage = () => {
const { userId, handleLogOut } = useContext(AuthContext)
return (
<section>
<h5>Your ID is: <span>{userId}</span></h5>
<button className="btn-logout" onClick={handleLogOut}>Log out</button>
</section>
)
}
export const AuthPage = () => {
return (
<section>
<Login />
<Register />
</section>
)
}
🔥 Conclusión.
Sin duda usar un servio como Firebase que nos ayuda mucho en ahorrar tiempo a comparación de construir nuestros propios servicios como la autenticación.
Espero que te haya gustado esta publicación y que te haya ayudada a entender más sobre como realizar la autenticación de usuarios en tus aplicación con React. 🤗
Si es que conoces alguna otra forma distinta o mejor de realizar esta funcionalidad con gusto puedes comentarla 🙌.
Te invito a que revises mi portafolio en caso de que estés interesado en contactarme para algún proyecto! Franklin Martinez Lucas
🔵 No olvides seguirme también en twitter: @Frankomtz361
🔥 Código fuente.
Franklin361 / auth-firebase-react
Application to show how to do authentication with Firebase and React JS 🔑
React app authentication using Firebase. 🔑
This time, we are going to implement authentication using React JS and Firebase!
Features ⚙️
- Log in.
- Log in with Google.
- Sign in
- Log out.
Technologies 🧪
-
▶️ React JS (v 18) -
▶️ Vite JS -
▶️ TypeScript -
▶️ Firebase (v 9.10) -
▶️ CSS vanilla
Installation 🧰
- Clone the repository (you need to have Git installed).
git clone https://github.com/Franklin361/auth-firebase-react
- Install dependencies of the project.
npm install
- Run the project.
npm run dev
Article links ⛓️
Here's the link to the tutorial in case you'd like to take a look at it! eyes 👀
-
🇲🇽 🔗
-
🇺🇲 🔗
Top comments (0)