CRUD - React and Google Firebase
Tags: react, firebase, nextjs, javascript
Published: 2022-09-27 14:39:22

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
```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 };
```

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

```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;
```
To figure out, how we can get ``` <value_here> ```. Please refer to this link [firebase.google.com](https://firebase.google.com/docs/web/setup?hl=en&authuser=0)

## III. Make a library to manage the file in fileManage.ts
```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);
    }),
  );
}
```
| 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

```tsx
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;
```
### V. That it's. Hope you doing great.
Thank for reading
Have a good day.

Thang Em No Dev.