Search code examples
javascriptreactjssupabasesupabase-database

How to display an image that is stored in a database with React and Supabase?


Good day, community. I am developing a registration module with React and Suspabase. I have a form where the user selects an image and it is saved correctly in a supabase bucket. Now I want to make the image show to the user once it is saved in the database that went up, I really don't have much idea how to do this I've never used buckets in supabase and I've already read the documentation but I'm still lost I hope you can help me.

This is the code I have at the momento:

Componente CrudUser.js

import React, { useEffect, useState } from "react";
import { supabase } from "../supabase/client";
import RegisterFormUser from "./RegisterFormUser";
import SignOut from "./SignOut";
import TableUser from "./TableUser";

const CrudUser = () => {
  const [db, setDb] = useState({});
  const [dataRol, setDataRol] = useState({});
  const [nameRol, setNameRol] = useState({});
  const [dataUserRol, setDataUserRol] = useState({});
  const [dataToEdit, setDataToEdit] = useState(null);


  //Operacion de lectura tabla Users
  const getUserData = () => {
    try {
      supabase.auth.getUser().then(async (value) => {
        const user = value.data.user;
        const { data } = await supabase
          .from("users")
          .select()
          .eq("email", user.email);
        setDb(data);
      });
    } catch (error) {
      console.error(error.message);
    }
  };

  //Operacion de lectura tabla Roles
  const getRoles = async () => {
    try {
      const { data } = await supabase.from("roles").select("*");
      setDataRol(data);
    } catch (error) {
      console.error(error);
    }
  };

  //Operación de lectura para traer el rol del usuario logueado
  const getUserRole = async () => {
    try {
      await supabase.auth.getUser().then(async (value) => {
        const user = value.data.user;
        const { data } = await supabase
          .from("users")
          .select("roles(nombre)")
          .eq("email", user.email);

        data.map((el) => el.roles.map((roles) => setNameRol(roles.nombre)));
      });
    } catch (error) {
      console.log(error.message);
    }
  };

  const getDataTabRolesUser = async () => {
    try {
      const { data } = await supabase.from("users_roles").select("*");
      setDataUserRol(data);
    } catch (error) {
      console.error(error.message);
    }
  };



  useEffect(() => {
    getUserData();
    getRoles();
    getDataTabRolesUser();
  }, []);


  getUserRole();

  const createDataUser = (data) => {
    try {
      supabase.auth.getUser().then(async (value) => {
        const user = value.data.user;
        data.user_id = Date.now();
        await supabase.from("users").insert({
          user_id: data.user_id,
          email: user.email,
          nombres: data.nombres,
          apellidos: data.apellidos,
          fecha_nacimiento: data.fecha_nacimiento,
          is_active: true,
          avatar: data.avatar,
        });
        console.log("Register added successfully");
        setDb([...db, data]);
      });
    } catch (error) {
      console.error(error.message);
    }
  };

  const createRolesUser = async (data) => {
    try {
      await supabase
        .from("users_roles")
        .insert({ user_id: data.user_id, rol_id: data.roles });
      console.log("Rol added succesfully");
      setDataUserRol([...dataUserRol, data]);
    } catch (error) {
      console.error(error.message);
    }
  };

  const uploadAvatar = async (data) => {
    const file = data.infoAvatar;
    const fileName = data.avatar;

    let { error: uploadError } = await supabase.storage
      .from("avatars")
      .upload(fileName, file);

    if (uploadError) {
      throw uploadError;
    }
  };

  const deleteDataUser = async (id) => {
    let isDelete = window.confirm("Are you sure to delete your data?");
    if (isDelete) {
      try {
        await supabase.from("users_roles").delete().eq("user_id", id);
        await supabase.from("users").delete().eq("user_id", id);

        let newData = db.filter((el) => el.user_id !== id);
        setDb(newData);

        console.log("The record was deleted");
      } catch (error) {
        console.error(error.message);
      }
    } else {
      return;
    }
  };

  const updateDataUser = async (data, updateFields) => {
    const { error } = await supabase
      .from("users")
      .update(updateFields)
      .eq("user_id", data.user_id);

    let newData = db.map((el) => (el.user_id === data.user_id ? data : el));
    setDb(newData);

    if (error) throw error;
    console.log(data);
  };

  const updateRolesUser = async (data, updateFields) => {
    const { error } = await supabase
      .from("users_roles")
      .update(updateFields)
      .eq("user_id", data.user_id);

    if (error) throw error;
    console.log(data);
  };

  return (
    <div>
      <div className="grid-1-2">
        <RegisterFormUser
          data={dataRol}
          userData={db}
          createDataUser={createDataUser}
          createRolesUser={createRolesUser}
          dataToEdit={dataToEdit}
          updateDataUser={updateDataUser}
          updateRolesUser={updateRolesUser}
          setDataToEdit={setDataToEdit}
          uploadAvatar={uploadAvatar}
        />
        <br />
        <br />
        <TableUser
          data={db}
          dataRol={nameRol}
          setDataToEdit={setDataToEdit}
          deleteDataUser={deleteDataUser}
        />
        <SignOut />
      </div>
    </div>
  );
};

export default CrudUser;

Component RegisterFormUser.js

import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { supabase } from "../supabase/client";

const initialForm = {
  user_id: null,
  nombres: "",
  apellidos: "",
  fecha_nacimiento: "",
  avatar: "",
  roles: null,
  infoAvatar: "",
};

function RegisterFormUser(props) {
  //Props
  const {
    data,
    userData,
    createDataUser,
    createRolesUser,
    dataToEdit,
    updateDataUser,
    updateRolesUser,
    setDataToEdit,
    uploadAvatar,
  } = props;

  //States
  const [form, setForm] = useState(initialForm);
  const [message, setMessage] = useState();
  const [response, setResponse] = useState(null);
  const [selectValue, setSelectValue] = useState("");
  const [selectPicture, setSelectPicture] = useState("");


  //Navigate hook
  const navigate = useNavigate();

  //Set form Values from dataToEdit
  useEffect(() => {
    if (dataToEdit) {
      setForm(dataToEdit);
    } else {
      setForm(initialForm);
    }
  }, [dataToEdit]);

  //Redirect to signin if not logged in
  useEffect(() => {
    supabase.auth.getUser().then((value) => {
      if (!value.data.user) {
        navigate("/signin");
      }
    });
  }, [navigate]);

  //Handle form input changes
  const handleChange = (e) => {
    setForm({
      ...form,
      [e.target.name]: e.target.value,
    });
  };

  //Handle form submission
  const handleSubmit = (e) => {
    e.preventDefault();
    if (form.user_id === null) {
      createDataUser(form);
      setMessage("Register added successfully");
      setResponse(true);
      setTimeout(() => {
        setResponse(false);
      }, 3000);
      setTimeout(() => {
        createRolesUser(form);
      }, 200);
    } else if (form.user_id !== null) {
      updateDataUser(form, {
        nombres: form.nombres,
        apellidos: form.apellidos,
        fecha_nacimiento: form.fecha_nacimiento,
      });

      updateRolesUser(form, {
        rol_id: form.roles,
      });
    }
    setSelectValue("");
    setSelectPicture("");
    handleReset();
  };

  //Handle form reset
  const handleReset = (e) => {
    setForm(initialForm);
    setSelectValue("");
    setSelectPicture("");
    setDataToEdit(null);
  };

  //Handle select changes
  const handleSelectChange = (e) => {
    setSelectValue(e.target.value);
    form.roles = e.target.value;
  };

  //Handle seleccion pictures
  const handleSelectPicture = (e) => {
    setSelectPicture(e.target.value);
    form.avatar = e.target.files[0].name;
    form.infoAvatar = e.target.files[0];

    console.log(form.avatar);
    console.log(form.infoAvatar);
    uploadAvatar(form);
  };

  return (
    <div>
      <h1>Additional data</h1>
      {/*userData.length > 0 ? console.log(userData[0].avatar) : console.log("empty")*/}
      {dataToEdit || userData.length <= 0 ? (
        <form onSubmit={handleSubmit}>
          <h3>{dataToEdit ? "Edit" : "Add"}</h3>
          <label htmlFor="nombres">Name </label>
          <input
            type="text"
            id="nombres"
            name="nombres"
            onChange={handleChange}
            value={form.nombres}
            placeholder="Write your Name"
          />
          <br />
          <br />
          <label htmlFor="apellidos">Last names </label>
          <input
            type="text"
            id="apellidos"
            name="apellidos"
            onChange={handleChange}
            value={form.apellidos}
            placeholder="Write your last name"
          />
          <br />
          <br />
          <label htmlFor="fecha_nacimiento">birthdate </label>
          <input
            type="date"
            id="fecha_nacimiento"
            name="fecha_nacimiento"
            onChange={handleChange}
            value={form.fecha_nacimiento}
          />
          <br />
          <br />
          <label htmlFor="roles">Roles </label>
          <select id="roles" onChange={handleSelectChange} value={selectValue}>
            <option value="">Select your role</option>
            {data.length > 0 ? (
              data.map((el, index) => (
                <option key={index} value={el.roles_id}>
                  {el.nombre}
                </option>
              ))
            ) : (
              <option value="" disabled>
                Loading...
              </option>
            )}
          </select>
          <br />
          <br />
          <label> Choose a profile picture</label>
          <input
            type="file"
            id="avatar"
            name="avatar"
            accept="image/png, image/jpeg"
            value={selectPicture}
            onChange={handleSelectPicture}
          />
          <br />
          <br />
          <input type="submit" value={dataToEdit ? "Edit" : "Add"} />
          <input type="reset" value="Clear" onClick={handleReset} />
        </form>
      ) : (
        <h3>Your data</h3>
      )}
      {response && <p>{message}</p>}
    </div>
  );
}

export default RegisterFormUser;

Solution

  • In order to display an image, you just have to get its URL and pass it to a img element.

    First, add a imageUrl state at the top.

    const [imageUrl, setImageUrl] = useState()
    

    Then after uploading, get the image URL and set it as the state. This code assumes you are using a public bucket, but you just if you are using a private bucket you just have to change the getPublicUrl to createSignedUrl

      const uploadAvatar = async (data) => {
        const file = data.infoAvatar;
        const fileName = data.avatar;
    
        let { error: uploadError } = await supabase.storage
          .from("avatars")
          .upload(fileName, file);
    
        if (uploadError) {
          throw uploadError;
        }
    
        
        const { data } = supabase
          .storage
          .from('avatars')
          .getPublicUrl(fileName)
        setImageUrl(data.publicUrl)
    
      };
    

    Then somewhere within your UI you just need a img element with the URL set to imageUrl state.

    <img src={imageUrl} />