Demystifying Firebase (Part-3)

June 22, 2024 (5mo ago)

Introduction

Looking for a database solution that keeps your app running smoothly, even offline? Enter Firestore, the cloud-based storage system that effortlessly syncs data across devices and offers real-time updates. In Part 3 of our series, we'll explore Firestore setup and learn how to leverage its power in our React Native projects.

Cloud Firestore Setup

Start by installing the Firestore package in your project:

npm install @react-native-firebase/firestore

Image description

Image description

Image description

Image description

Image description

Image description

Setting Up Our Utility functions

To simplify Firestore operations, let's set up utility functions in a firestore.js file. These functions will handle CRUD operations and make data management a breeze.

 
import firestore from '@react-native-firebase/firestore';
 
const rootCollection = firestore();
 
export const getAllDocuments = async (collectionName) => {
    try {
        const collectionRef = rootCollection.collection(collectionName);
 
 
 
        const querySnapshot = await collectionRef.get();
        const collectionData = querySnapshot.docs.map(doc => ({
            id: doc.id,
            ...doc.data(),
        }));
 
        return collectionData;
    } catch (error) {
        console.error(`Error fetching ${collectionName} data:`, error);
        throw error;
    }
};
 
export const createDocument = async (collectionName, documentData) => {
    try {
        const documentRef = rootCollection.collection(collectionName).doc();
        const serverTimestamp = firestore.FieldValue.serverTimestamp();
 
        const dataWithTimestamps = {
            ...documentData,
            createdAt: serverTimestamp,
            modifiedAt: serverTimestamp,
        };
 
        await documentRef.set(dataWithTimestamps);
        return documentRef;
    } catch (error) {
        console.error('Error creating document:', error);
        throw error;
    }
};
 
export const readDocumentData = async (collectionName, documentId) => {
    try {
        const documentSnapshot = await rootCollection.collection(collectionName).doc(documentId).get();
 
        if (documentSnapshot.exists) {
            return documentSnapshot.data();
        } else {
            return null;
        }
    } catch (error) {
        console.error('Error reading document data:', error);
        throw error;
    }
};
 
export const updateDocument = async (collectionName, documentId, updatedDocumentData) => {
    try {
        const serverTimestamp = firestore.FieldValue.serverTimestamp();
 
        const dataWithTimestamps = {
            ...updatedDocumentData,
            modifiedAt: serverTimestamp,
        };
 
        return rootCollection.collection(collectionName).doc(documentId).update(dataWithTimestamps);
    } catch (error) {
        console.error('Error updating document:', error);
        throw error;
    }
};
 
export const deleteDocument = async (collectionName, documentId) => {
    try {
        return rootCollection.collection(collectionName).doc(documentId).delete();
    } catch (error) {
        console.error('Error deleting document:', error);
        throw error;
    }
};

Simple Posts Screen

Now, let's put Firestore to work by creating a simple posts screen with complete CRUD operations. From fetching posts to adding, editing, and deleting them, you'll learn how to harness Firestore's flexibility and scalability.

import {View, Text, Button, FlatList} from 'react-native';
import React, {useEffect} from 'react';
import {
  getAllDocuments,
  createDocument,
  updateDocument,
  deleteDocument,
} from '../Firebase/firestore';
import { get } from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
 
const PostsScreen = ({navigation}) => {
 
    // State to store all posts
  const [posts, setPosts] = React.useState([]);
 
 
  // Fetch all posts from Firestore
  const getAllPosts = async () => {
    try {
      const fetchedPosts = await getAllDocuments('posts');
      console.log(fetchedPosts);
 
      setPosts(fetchedPosts || []);
    } catch (error) {
      console.error('Error fetching posts:', error);
    }
 
  };
 
  // Add a new post to Firestore
  const addPost = async () => {
 
    try {
      await createDocument('posts', {
        title: 'New Post',
        content: 'This is a new post',
      });
      getAllPosts();
    } catch (error) {
      console.error('Error adding post:', error);
    }
  };
 
    // Delete a post from Firestore
  const deletePost = async postId => {
    try {
      await deleteDocument('posts', postId);
      getAllPosts();
    } catch (error) {
      console.error('Error deleting post:', error);
    }
  };
 
    // Update a post in Firestore
  const updatePost = async postId => {
    console.log('Updating post:', postId);
    try {
      await updateDocument('posts', postId, {
        title: 'Updated Post',
        content: 'This post has been updated',
      });
      const updatedPosts = posts.map(post => {
        if (post.id === postId) {
          return {
            id: postId,
            title: 'Updated Post',
            content: 'This post has been updated',
          };
        }
        return post;
      });
      getAllPosts();
    } catch (error) {
      console.error('Error updating post:', error);
    }
  };
 
 
  // Fetch all posts from Firestore when the component mounts
  useEffect(() => {
    getAllPosts();
  }, []);
 
  return (
    <View style={{flex: 1, alignItems: 'center'}}>
      <Button title="Add Post" onPress={addPost} />
      <Text
        style={{
          color: 'black',
          margin: 10,
          padding: 10,
          fontSize: 20,
          fontWeight: 'bold',
        }}>
        Posts
      </Text>
 
      <FlatList
        data={posts}
        ListEmptyComponent={() => (
          <View style={{justifyContent: 'center', alignItems: 'center'}}>
            <Text style={{color: 'black', fontSize: 18}}>No Posts Found</Text>
          </View>
        )}
        renderItem={({item}) => (
          <View
            style={{
              margin: 10,
              padding: 10,
              borderColor: 'black',
              borderWidth: 1,
              flexDirection: 'column',
            }}>
            <Text style={{color: "black", fontSize: 16, marginBottom:5}}>{item.title}</Text>
            <Text style={{color: "gray", fontSize: 12, marginBottom:10}}>{item.content}</Text>
 
            <Button
              title="Edit Post"
              onPress={() => updatePost(item.id)}
              color={'purple'}
            />
 
            <Button
              title="Delete Post"
              onPress={() => deletePost(item.id)}
              color={'red'}
            />
          </View>
        )}
        keyExtractor={(item, index) => (item.id || index).toString()}
      />
    </View>
  );
};
 
export default PostsScreen;
 

End Result


If you found value in this article, I'd love to hear your thoughts in the comments section! Your feedback fuels my motivation to create more content. If you're interested in learning more about Firestore or have specific topics you'd like me to cover, don't hesitate to let me know. Your input helps shape future articles and ensures they're tailored to your interests and needs. Thank you for reading!

Source : React Native Firebase