DEV Community

Cover image for CRUD - React and Google Firebase
Em Ha Tuan
Em Ha Tuan

Posted on • Edited on

CRUD - React and Google Firebase

Hi, It's me again.
In this post, I share to you how can I manage or CRUD the file in google firebase - storage with React.

Create react app with nextjs framework

create-next-app nextjs-crud-gg --ts

Installing the libraries

npm i -s firebase uuid && npm i --dev @types/uuid

Creating the structure of project.

  1. storage.ts in libs folder - (root_project)/libs/storage.ts
  2. fileManage.ts in utils folder - (root_project)/utils/fileManage.ts
  3. Add env variable to next.config.js
  4. Custom the index.tsx page - We just use one page to CRUD the file as the demo page.

I. Setup google firebase storage in storage.ts

import { initializeApp } from 'firebase/app';
import { getStorage } from 'firebase/storage';

const firebaseConfig = {
  apiKey: process.env.GOOGLE_API_KEY,
  authDomain: process.env.GOOGLE_AUTH_DOMAIN,
  projectId: process.env.GOOGLE_PROJECT_ID,
  storageBucket: process.env.GOOGLE_STORAGE_BUCKET,
  messagingSenderId: process.env.MESSAGING_SENDER_ID,
  appId: process.env.GOOGLE_APP_ID,
  measurementId: process.env.GOOGLE_MEASUREMENT_ID,
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const storage = getStorage(app);

export { storage };
Enter fullscreen mode Exit fullscreen mode

II. Setup environment variables in the next.config.js

/** @type {import('next').NextConfig} */

const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  env: {
    GOOGLE_API_KEY: <value_here>,
    GOOGLE_AUTH_DOMAIN:  <value_here>,
    GOOGLE_PROJECT_ID: <value_here>,
    GOOGLE_STORAGE_BUCKET: <value_here>,
    MESSAGING_SENDER_ID: <value_here>,
    GOOGLE_APP_ID:  <value_here>,
    GOOGLE_MEASUREMENT_ID:  <value_here>,
  },
};
module.exports = nextConfig;
Enter fullscreen mode Exit fullscreen mode

To figure out, how we can get

<value_here>

. Please refer to this link firebase.google.com

III. Make a library to manage the file in fileManage.ts

import { storage } from '../libs/storage';
import {
  deleteObject,
  getDownloadURL,
  listAll,
  ref,
  uploadBytes,
  UploadResult,
} from 'firebase/storage';
import { v4 } from 'uuid';

/**
 * Get all file by folder name.
 *
 * @param string folder image.
 * @returns array File of url.
 */
export async function getAllFile(folder: string) {
  const filesRef = ref(storage, `${folder}`);
  const files = (await listAll(filesRef)).items;
  return Promise.all(
    files.map(async (fileRef) => {
      return await getDownloadURL(fileRef);
    }),
  );
}

/**
 * Get file by url.
 *
 * @param string url image.
 * @returns boolean true if deleted file.
 */
export async function getFile(url: string) {
  const fileRef = ref(storage, url);
  return await getDownloadURL(fileRef);
}

/**
 * Delete file by url.
 *
 * @param string url image.
 * @returns boolean true if deleted file.
 */
export async function deleteFile(url: string) {
  const fileRef = ref(storage, url);
  await deleteObject(fileRef);
}

/**
 * Upload file to google firebase storage.
 *
 * @param string folder name.
 * @param array filesUpload list of file
 * @returns array list of url file.
 */
export async function uploadFile(folder: string, filesUpload: File[]) {
  return Promise.all(
    [...filesUpload].map(async (file: File) => {
      const fileRef = ref(storage, `${folder}/${file.name + v4()}`);
      const value: UploadResult = await uploadBytes(fileRef, file);
      return await getDownloadURL(value.ref);
    }),
  );
}
Enter fullscreen mode Exit fullscreen mode
Method Description
getAllFile Get all the file in google firebase storage base the the fodler name
getFile Get single file based on the url of file
deleteFile Delete file based on the url of file
uploadFile Upload multiple file
updateFile Well, we can delete the file which uploaded before we upload the new file. That make easy and simple. We can use the methods: "deleteFile" + "uploadFile".

VI. Manage the file with React

I will edit the index.tsx page

import type { NextPage } from 'next';
import { useRef, useState } from 'react';
import { deleteFile, getAllFile, uploadFile } from '../utils/fileManager';

const Home: NextPage = () => {
  // Hook for upload the file
  const [images, setImages] = useState<File[]>([]);
  // Hook for preview the file uploaded.
  const [imagesUploaded, setImageUploaded] = useState<string[]>([]);
  // Hook for list all the file in the "images" folder.
  const [imageGallery, setImageGallery] = useState<string[]>([]);
  // Use fileRef to clean the file after uploaded.
  const fileRef = useRef<HTMLInputElement>(null);
  // Creating the folder in google firebase storage. I named the "images" for contain only the type of image.
  const FOLDER_NAME = 'images';

  /**
   * Add file uploaded
   * @param e Event HTML Input Change
   */
  const handleOnChange = (e: any) => {
    setImages(e.target.files as File[]);
    e.target.files = null;
  };

  /**
   * Delete the file with url file.
   * @param url string
   */
  const handleOnDelete = (url: string) => {
    deleteFile(url).then(() => {
      setImageUploaded((prev) => prev.filter((img) => img !== url));
      setImageGallery((prev) => prev.filter((img) => img !== url));
      alert(`Deleted file`);
    });
  };

  /**
   * Upload file, and show alert if success.
   */
  const handleOnUpload = () => {
    if (images.length === 0) return false;
    uploadFile(FOLDER_NAME, images).then((imageList) => {
      setImageUploaded(imageList);
      alert('Upload file successed');
      if (fileRef.current) {
        fileRef.current.value = '';
      }
    });
  };

  /**
   * Get all the files base on the folder name.
   */
  const handleGetFiles = () => {
    getAllFile(FOLDER_NAME)
      .then((listImages) => {
        setImageGallery(listImages);
      })
      .catch((err) => {
        console.log('Something went wrong', err);
      });
  };

  return (
    <div className="app">
      <div className="form-control">
        <label htmlFor="file">
          <input
            type="file"
            ref={fileRef}
            onChange={handleOnChange}
            multiple
          />
        </label>
        <button
          className="button"
          onClick={handleOnUpload}
        >
          Upload file to firebase storage
        </button>
      </div>
      <div className="image-container">
        <p>Image preview</p>
        <ul className="image-container__list">
          {imagesUploaded.length > 0 &&
            imagesUploaded.map((image) => (
              <li
                style={{ listStyle: 'none' }}
                key={image}
              >
                <img
                  src={image}
                  width="100%"
                />
                <button
                  type="button"
                  onClick={() => handleOnDelete(image)}
                >
                  Delete file
                </button>
              </li>
            ))}
        </ul>
      </div>
      <div className="image-container-gallery">
        <h1>
          Image Gallery
          <button
            className="button"
            onClick={handleGetFiles}
          >
            Click me to get it !!!
          </button>
        </h1>
        <div className="image-container">
          <ul className="image-container__list">
            {imageGallery.length > 0 &&
              imageGallery.map((image) => (
                <li
                  style={{ listStyle: 'none' }}
                  key={image}
                >
                  <img
                    src={image}
                    width="100%"
                  />
                  <button
                    type="button"
                    onClick={() => handleOnDelete(image)}
                  >
                    Delete file
                  </button>
                </li>
              ))}
          </ul>
        </div>
      </div>
    </div>
  );
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

V. That it's. Hope you doing great.

Thank for reading
Have a good day.

Thang Em No Dev.

Top comments (0)