Connecting React with Firebase

Loading

Firebase provides a powerful backend solution for React applications, offering authentication, realtime databases, file storage, and more. Here’s how to integrate Firebase with your React app:

1. Firebase Setup

Install Firebase SDK

npm install firebase
# or
yarn add firebase

Initialize Firebase

// src/firebase.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import { getStorage } from "firebase/storage";

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const db = getFirestore(app);
export const storage = getStorage(app);

2. Authentication

Email/Password Authentication

import { auth } from "./firebase";
import { 
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut
} from "firebase/auth";

// Sign up
const signUp = async (email, password) => {
  try {
    const userCredential = await createUserWithEmailAndPassword(
      auth, 
      email, 
      password
    );
    console.log("User created:", userCredential.user);
  } catch (error) {
    console.error("Signup error:", error.message);
  }
};

// Sign in
const signIn = async (email, password) => {
  try {
    const userCredential = await signInWithEmailAndPassword(
      auth,
      email,
      password
    );
    console.log("User signed in:", userCredential.user);
  } catch (error) {
    console.error("Login error:", error.message);
  }
};

// Sign out
const logOut = async () => {
  try {
    await signOut(auth);
    console.log("User signed out");
  } catch (error) {
    console.error("Logout error:", error.message);
  }
};

Google Authentication

import { GoogleAuthProvider, signInWithPopup } from "firebase/auth";

const signInWithGoogle = async () => {
  const provider = new GoogleAuthProvider();
  try {
    const result = await signInWithPopup(auth, provider);
    console.log("Google user:", result.user);
  } catch (error) {
    console.error("Google sign-in error:", error.message);
  }
};

3. Firestore Database

CRUD Operations

import { db } from "./firebase";
import {
  collection,
  addDoc,
  getDocs,
  doc,
  updateDoc,
  deleteDoc
} from "firebase/firestore";

// Add document
const addPost = async (title, content) => {
  try {
    const docRef = await addDoc(collection(db, "posts"), {
      title,
      content,
      createdAt: new Date()
    });
    console.log("Document written with ID:", docRef.id);
  } catch (error) {
    console.error("Error adding document:", error);
  }
};

// Read documents
const getPosts = async () => {
  const querySnapshot = await getDocs(collection(db, "posts"));
  const posts = [];
  querySnapshot.forEach((doc) => {
    posts.push({ id: doc.id, ...doc.data() });
  });
  return posts;
};

// Update document
const updatePost = async (id, newData) => {
  try {
    await updateDoc(doc(db, "posts", id), newData);
    console.log("Document updated");
  } catch (error) {
    console.error("Error updating document:", error);
  }
};

// Delete document
const deletePost = async (id) => {
  try {
    await deleteDoc(doc(db, "posts", id));
    console.log("Document deleted");
  } catch (error) {
    console.error("Error deleting document:", error);
  }
};

4. Realtime Updates

Subscribe to Data Changes

import { onSnapshot } from "firebase/firestore";

const setupPostsListener = () => {
  const unsubscribe = onSnapshot(collection(db, "posts"), (snapshot) => {
    const posts = [];
    snapshot.forEach((doc) => {
      posts.push({ id: doc.id, ...doc.data() });
    });
    console.log("Current posts:", posts);
  });

  // Call unsubscribe() to stop listening when needed
  return unsubscribe;
};

5. File Storage

Upload Files

import { storage } from "./firebase";
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";

const uploadFile = async (file) => {
  try {
    // Create a storage reference
    const storageRef = ref(storage, `files/${file.name}`);

    // Upload file
    const snapshot = await uploadBytes(storageRef, file);
    console.log("Uploaded file:", snapshot.metadata);

    // Get download URL
    const downloadURL = await getDownloadURL(storageRef);
    console.log("File available at:", downloadURL);
    return downloadURL;
  } catch (error) {
    console.error("Upload failed:", error);
    throw error;
  }
};

6. Authentication State Management

React Context for Auth

// AuthContext.js
import { createContext, useContext, useEffect, useState } from "react";
import { auth } from "./firebase";
import { onAuthStateChanged } from "firebase/auth";

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [currentUser, setCurrentUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setCurrentUser(user);
      setLoading(false);
    });

    return unsubscribe;
  }, []);

  const value = {
    currentUser,
    loading
  };

  return (
    <AuthContext.Provider value={value}>
      {!loading && children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  return useContext(AuthContext);
}

Protected Routes

import { useAuth } from "./AuthContext";
import { Navigate } from "react-router-dom";

function ProtectedRoute({ children }) {
  const { currentUser } = useAuth();

  if (!currentUser) {
    return <Navigate to="/login" replace />;
  }

  return children;
}

7. Security Rules

Example Firestore Rules

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{postId} {
      allow read: if true;
      allow create: if request.auth != null;
      allow update, delete: if request.auth != null && 
                           request.auth.uid == resource.data.authorId;
    }
  }
}

Example Storage Rules

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /files/{fileName} {
      allow read: if true;
      allow write: if request.auth != null && 
                    request.resource.size < 5 * 1024 * 1024;
    }
  }
}

Best Practices

  1. Environment Variables: Store Firebase config in .env file
   REACT_APP_FIREBASE_API_KEY=your_api_key
   REACT_APP_FIREBASE_AUTH_DOMAIN=your_auth_domain
  1. Error Handling: Implement comprehensive error handling
  2. Loading States: Show loading indicators during async operations
  3. Security: Always implement proper security rules
  4. Modularization: Organize Firebase services into separate modules
  5. TypeScript: Use Firebase with TypeScript for better type safety
  6. Performance: Optimize queries with indexes and pagination

This comprehensive integration covers the core Firebase services you’ll typically need in a React application. Remember to enable the Firebase services you need in the Firebase Console before implementing them in your app.

Leave a Reply

Your email address will not be published. Required fields are marked *