ReactFire
Conecta React con Firebase de manera sencilla usando hooks
ReactFire es una librería que te permite usar Firebase en tus aplicaciones React de manera fácil y natural usando hooks. Perfecta para estudiantes y desarrolladores que quieren integrar autenticación, base de datos y almacenamiento en sus proyectos.
📦 Instalación
Para empezar a usar ReactFire en tu proyecto, necesitas instalarlo junto con Firebase:
Con npm:
npm install reactfire firebase
Con yarn:
yarn add reactfire firebase
💡 Nota: ReactFire requiere React 16.8+ para usar hooks y Firebase SDK v9+
⚙️ Configuración Inicial
Antes de usar ReactFire, necesitas configurar tu aplicación con Firebase:
1. Obtén tu configuración de Firebase
Ve a la consola de Firebase y crea un proyecto. Luego obtén tu configuración desde la configuración del proyecto.
2. Configura ReactFire en tu aplicación
// App.tsx
import React from 'react';
import { FirebaseAppProvider } from 'reactfire';
import { MiComponente } from './MiComponente';
// Tu configuración de Firebase
const firebaseConfig = {
apiKey: "tu-api-key",
authDomain: "tu-proyecto.firebaseapp.com",
projectId: "tu-proyecto",
storageBucket: "tu-proyecto.appspot.com",
messagingSenderId: "123456789",
appId: "tu-app-id"
};
function App() {
return (
<FirebaseAppProvider firebaseConfig={firebaseConfig}>
<MiComponente />
</FirebaseAppProvider>
);
}
export default App;
3. Configura los proveedores de servicios
// Configuradores.tsx
import React from 'react';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { getStorage } from 'firebase/storage';
import {
AuthProvider,
FirestoreProvider,
StorageProvider,
useFirebaseApp
} from 'reactfire';
function ConfiguradorServicios({ children }) {
const app = useFirebaseApp();
// Inicializar servicios
const auth = getAuth(app);
const firestore = getFirestore(app);
const storage = getStorage(app);
return (
<AuthProvider sdk={auth}>
<FirestoreProvider sdk={firestore}>
<StorageProvider sdk={storage}>
{children}
</StorageProvider>
</FirestoreProvider>
</AuthProvider>
);
}
🔐 Autenticación
ReactFire hace que manejar la autenticación sea súper fácil con hooks intuitivos.
Hooks principales para autenticación:
useAuth()
- Obtiene la instancia de AuthuseUser()
- Obtiene el usuario actual (con Suspense)useSigninCheck()
- Verifica si hay un usuario autenticado
Ejemplo: Componente de Login/Logout
import React from 'react';
import { useAuth, useSigninCheck } from 'reactfire';
import { GoogleAuthProvider, signInWithPopup, signOut } from 'firebase/auth';
function ComponenteAuth() {
const auth = useAuth();
const { data: signInCheckResult } = useSigninCheck();
// Función para iniciar sesión con Google
const iniciarSesion = async () => {
const provider = new GoogleAuthProvider();
try {
await signInWithPopup(auth, provider);
console.log('¡Sesión iniciada!');
} catch (error) {
console.error('Error al iniciar sesión:', error);
}
};
// Función para cerrar sesión
const cerrarSesion = () => {
signOut(auth);
};
// Si está cargando
if (signInCheckResult === undefined) {
return <div>Verificando autenticación...</div>;
}
// Si está autenticado
if (signInCheckResult.signedIn) {
return (
<div>
<h3>¡Bienvenido!</h3>
<button onClick={cerrarSesion}>
Cerrar Sesión
</button>
</div>
);
}
// Si no está autenticado
return (
<div>
<h3>Por favor, inicia sesión</h3>
<button onClick={iniciarSesion}>
Iniciar con Google
</button>
</div>
);
}
Ejemplo: Mostrar información del usuario
import React, { Suspense } from 'react';
import { useUser } from 'reactfire';
function DetallesUsuario() {
const { data: user } = useUser();
return (
<div>
<h3>Información del Usuario</h3>
<p><strong>Nombre:</strong> {user.displayName}</p>
<p><strong>Email:</strong> {user.email}</p>
<img src={user.photoURL} alt="Foto de perfil" />
</div>
);
}
// Úsalo con Suspense
function AppConUsuario() {
return (
<Suspense fallback={<div>Cargando usuario...</div>}>
<DetallesUsuario />
</Suspense>
);
}
Ejemplo: Proteger rutas
import React from 'react';
import { useSigninCheck } from 'reactfire';
function RutaProtegida({ children }) {
const { data: signInCheckResult } = useSigninCheck();
if (!signInCheckResult.signedIn) {
return <div>Debes iniciar sesión para ver este contenido</div>;
}
return children;
}
// Uso
function App() {
return (
<RutaProtegida>
<ComponentePrivado />
</RutaProtegida>
);
}
🗄️ Cloud Firestore
Firestore es la base de datos NoSQL de Firebase. ReactFire te permite leer y escribir datos de forma reactiva.
Hooks principales para Firestore:
useFirestore()
- Obtiene la instancia de FirestoreuseFirestoreDocData()
- Lee un documento en tiempo realuseFirestoreCollectionData()
- Lee una colección en tiempo realuseFirestoreDocDataOnce()
- Lee un documento una sola vez
Ejemplo: Leer un documento
import React, { Suspense } from 'react';
import { useFirestore, useFirestoreDocData } from 'reactfire';
import { doc } from 'firebase/firestore';
function PerfilUsuario({ userId }) {
const firestore = useFirestore();
// Referencia al documento
const perfilRef = doc(firestore, 'usuarios', userId);
// Hook para leer el documento en tiempo real
const { data: perfil } = useFirestoreDocData(perfilRef);
return (
<div>
<h3>Perfil de {perfil.nombre}</h3>
<p><strong>Email:</strong> {perfil.email}</p>
<p><strong>Edad:</strong> {perfil.edad}</p>
</div>
);
}
// Usar con Suspense
function AppPerfil() {
return (
<Suspense fallback={<div>Cargando perfil...</div>}>
<PerfilUsuario userId="user123" />
</Suspense>
);
}
Ejemplo: Leer una colección
import React, { Suspense } from 'react';
import { useFirestore, useFirestoreCollectionData } from 'reactfire';
import { collection, query, orderBy, limit } from 'firebase/firestore';
function ListaPublicaciones() {
const firestore = useFirestore();
// Crear consulta
const publicacionesQuery = query(
collection(firestore, 'publicaciones'),
orderBy('fechaCreacion', 'desc'),
limit(10)
);
// Hook para leer la colección
const { data: publicaciones } = useFirestoreCollectionData(publicacionesQuery, {
idField: 'id' // Incluye el ID del documento
});
return (
<div>
<h3>Últimas Publicaciones</h3>
{publicaciones.map(publicacion => (
<div key={publicacion.id} className="publicacion">
<h4>{publicacion.titulo}</h4>
<p>{publicacion.contenido}</p>
<small>Por: {publicacion.autor}</small>
</div>
))}
</div>
);
}
function App() {
return (
<Suspense fallback={<div>Cargando publicaciones...</div>}>
<ListaPublicaciones />
</Suspense>
);
}
Ejemplo: Escribir datos
import React, { useState } from 'react';
import { useFirestore } from 'reactfire';
import { doc, setDoc, addDoc, collection, updateDoc, increment } from 'firebase/firestore';
function EscribirDatos() {
const firestore = useFirestore();
const [mensaje, setMensaje] = useState('');
// Crear un nuevo documento con ID automático
const crearPublicacion = async () => {
try {
const docRef = await addDoc(collection(firestore, 'publicaciones'), {
titulo: 'Mi nueva publicación',
contenido: mensaje,
autor: 'Usuario Actual',
fechaCreacion: new Date()
});
console.log('Documento creado con ID:', docRef.id);
setMensaje('');
} catch (error) {
console.error('Error al crear publicación:', error);
}
};
// Actualizar un documento existente
const actualizarContador = async () => {
const contadorRef = doc(firestore, 'contadores', 'general');
try {
await updateDoc(contadorRef, {
visitas: increment(1)
});
console.log('Contador actualizado');
} catch (error) {
console.error('Error al actualizar contador:', error);
}
};
return (
<div>
<h3>Escribir Datos</h3>
<textarea
value={mensaje}
onChange={(e) => setMensaje(e.target.value)}
placeholder="Escribe tu mensaje..."
/>
<button onClick={crearPublicacion}>
Crear Publicación
</button>
<button onClick={actualizarContador}>
Incrementar Visitas
</button>
</div>
);
}
📁 Cloud Storage
Cloud Storage te permite almacenar y servir archivos como imágenes, videos y documentos.
Hooks principales para Storage:
useStorage()
- Obtiene la instancia de StorageuseStorageDownloadURL()
- Obtiene la URL de descarga de un archivouseStorageTask()
- Monitorea el progreso de una subida
Ejemplo: Subir una imagen
import React, { useState } from 'react';
import { useStorage, useStorageTask } from 'reactfire';
import { ref, uploadBytesResumable } from 'firebase/storage';
function SubirImagen() {
const storage = useStorage();
const [uploadTask, setUploadTask] = useState(null);
const [imagenRef, setImagenRef] = useState(null);
const handleFileChange = (event) => {
const archivo = event.target.files[0];
if (archivo) {
// Crear referencia al archivo
const nuevaRef = ref(storage, `imagenes/${archivo.name}`);
setImagenRef(nuevaRef);
// Crear tarea de subida
const tarea = uploadBytesResumable(nuevaRef, archivo);
// Configurar eventos
tarea.then(() => {
console.log('¡Subida completada!');
setUploadTask(null);
}).catch((error) => {
console.error('Error en la subida:', error);
setUploadTask(null);
});
setUploadTask(tarea);
}
};
return (
<div>
<h3>Subir Imagen</h3>
<input
type="file"
accept="image/*"
onChange={handleFileChange}
/>
{uploadTask && (
<ProgresoSubida
uploadTask={uploadTask}
storageRef={imagenRef}
/>
)}
</div>
);
}
Ejemplo: Monitorear progreso de subida
import React from 'react';
import { useStorageTask } from 'reactfire';
function ProgresoSubida({ uploadTask, storageRef }) {
const { data: progreso } = useStorageTask(uploadTask, storageRef);
const { bytesTransferred, totalBytes } = progreso;
const porcentaje = Math.round(100 * (bytesTransferred / totalBytes));
return (
<div>
<div className="barra-progreso">
<div
className="progreso"
style={{ width: `${porcentaje}%` }}
/>
</div>
<p>Progreso: {porcentaje}% ({bytesTransferred} / {totalBytes} bytes)</p>
</div>
);
}
Ejemplo: Mostrar imagen desde Storage
import React, { Suspense } from 'react';
import { useStorage, useStorageDownloadURL } from 'reactfire';
import { ref } from 'firebase/storage';
function MostrarImagen({ rutaImagen }) {
const storage = useStorage();
const imagenRef = ref(storage, rutaImagen);
// Obtener URL de descarga
const { data: urlDescarga } = useStorageDownloadURL(imagenRef);
return (
<div>
<h4>Mi Imagen:</h4>
<img
src={urlDescarga}
alt="Imagen desde Firebase Storage"
style={{ maxWidth: '300px', height: 'auto' }}
/>
</div>
);
}
function GaleriaImagenes() {
return (
<div>
<h3>Galería</h3>
<Suspense fallback={<div>Cargando imagen...</div>}>
<MostrarImagen rutaImagen="imagenes/foto1.jpg" />
</Suspense>
<Suspense fallback={<div>Cargando imagen...</div>}>
<MostrarImagen rutaImagen="imagenes/foto2.jpg" />
</Suspense>
</div>
);
}
💡 Ejemplos Prácticos
Aquí tienes algunos ejemplos completos que combinan diferentes servicios de Firebase:
Ejemplo: Chat en Tiempo Real
import React, { useState, Suspense } from 'react';
import {
useFirestore,
useFirestoreCollectionData,
useAuth,
useUser
} from 'reactfire';
import {
collection,
addDoc,
query,
orderBy,
limit,
serverTimestamp
} from 'firebase/firestore';
function ChatApp() {
return (
<div className="chat-app">
<h2>💬 Chat en Tiempo Real</h2>
<Suspense fallback={<div>Cargando chat...</div>}>
<ListaMensajes />
<FormularioMensaje />
</Suspense>
</div>
);
}
function ListaMensajes() {
const firestore = useFirestore();
const mensajesQuery = query(
collection(firestore, 'mensajes'),
orderBy('timestamp', 'desc'),
limit(50)
);
const { data: mensajes } = useFirestoreCollectionData(mensajesQuery, {
idField: 'id'
});
return (
<div className="lista-mensajes">
{mensajes.reverse().map(mensaje => (
<div key={mensaje.id} className="mensaje">
<strong>{mensaje.autor}:</strong> {mensaje.texto}
</div>
))}
</div>
);
}
function FormularioMensaje() {
const [texto, setTexto] = useState('');
const firestore = useFirestore();
const { data: user } = useUser();
const enviarMensaje = async (e) => {
e.preventDefault();
if (texto.trim()) {
await addDoc(collection(firestore, 'mensajes'), {
texto: texto,
autor: user.displayName || 'Anónimo',
authorId: user.uid,
timestamp: serverTimestamp()
});
setTexto('');
}
};
return (
<form onSubmit={enviarMensaje} className="formulario-mensaje">
<input
value={texto}
onChange={(e) => setTexto(e.target.value)}
placeholder="Escribe tu mensaje..."
maxLength={200}
/>
<button type="submit">Enviar</button>
</form>
);
}
Ejemplo: Lista de Tareas Colaborativa
import React, { useState, Suspense } from 'react';
import {
useFirestore,
useFirestoreCollectionData,
useUser
} from 'reactfire';
import {
collection,
addDoc,
updateDoc,
deleteDoc,
doc,
query,
where
} from 'firebase/firestore';
function AppTareas() {
return (
<div className="app-tareas">
<h2>📝 Lista de Tareas Colaborativa</h2>
<Suspense fallback={<div>Cargando tareas...</div>}>
<FormularioTarea />
<ListaTareas />
</Suspense>
</div>
);
}
function FormularioTarea() {
const [titulo, setTitulo] = useState('');
const firestore = useFirestore();
const { data: user } = useUser();
const crearTarea = async (e) => {
e.preventDefault();
if (titulo.trim()) {
await addDoc(collection(firestore, 'tareas'), {
titulo,
completada: false,
autorId: user.uid,
autorNombre: user.displayName,
fechaCreacion: new Date()
});
setTitulo('');
}
};
return (
<form onSubmit={crearTarea}>
<input
value={titulo}
onChange={(e) => setTitulo(e.target.value)}
placeholder="Nueva tarea..."
/>
<button type="submit">Agregar</button>
</form>
);
}
function ListaTareas() {
const firestore = useFirestore();
const tareasQuery = query(
collection(firestore, 'tareas')
);
const { data: tareas } = useFirestoreCollectionData(tareasQuery, {
idField: 'id'
});
const toggleCompletada = async (tareaId, completada) => {
const tareaRef = doc(firestore, 'tareas', tareaId);
await updateDoc(tareaRef, { completada: !completada });
};
const eliminarTarea = async (tareaId) => {
const tareaRef = doc(firestore, 'tareas', tareaId);
await deleteDoc(tareaRef);
};
return (
<div className="lista-tareas">
{tareas.map(tarea => (
<div key={tarea.id} className={`tarea ${tarea.completada ? 'completada' : ''}`}>
<input
type="checkbox"
checked={tarea.completada}
onChange={() => toggleCompletada(tarea.id, tarea.completada)}
/>
<span>{tarea.titulo}</span>
<small>por {tarea.autorNombre}</small>
<button onClick={() => eliminarTarea(tarea.id)}>
🗑️
</button>
</div>
))}
</div>
);
}
💡 Consejos y Mejores Prácticas
🎣 Usa Suspense
Siempre envuelve componentes que usan hooks de datos con Suspense para mostrar estados de carga elegantes.
🔐 Reglas de Seguridad
Nunca olvides configurar las reglas de seguridad en Firebase para proteger tus datos.
📱 Datos en Tiempo Real
Los hooks como useFirestoreDocData
se actualizan automáticamente cuando cambian los datos en Firebase.
⚡ Optimización
Usa useFirestoreDocDataOnce
cuando solo necesites leer datos una vez, no en tiempo real.