skip to Main Content

Below is the my profile page code I thinking I should use a callback along with another useEffect function but I’m not sure please help. You ignore all the code involving deleting, saving, and handling ingredients I just working on the profile card for right now plus those already work. I really just don’t want the user to have to refresh the page every time they upload a new image to see it. Unless you all have other fun way of getting around this or suggest i just make another modal telling the user to refresh the page or any other creative way to make this feature seem less bad and unprofessional. I’m a beginner and would really appreciate some help with this!

import React, { useEffect, useState, useCallback } from "react";
import Axios from "axios";
import Cookies from "js-cookie";
import Footer from "./Footer";
import Header from "./Header";
import "./Profile.css";

const Profile = () => {
  const [userData, setUserData] = useState({});
  const [savedIngredients, setSavedIngredients] = useState([]);
  const [ingredientName, setIngredientName] = useState("");
  const [ingredientQuantity, setIngredientQuantity] = useState("");
  const [selectedFile, setSelectedFile] = useState(null);
  const [showUploadModal, setShowUploadModal] = useState(false);
  const [previewImage, setPreviewImage] = useState(null);
  const [profilePictureURL, setProfilePictureURL] = useState(userData.profilePicture);

  const API = "http://localhost:3000"

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    setSelectedFile(file);

    const previewImageURL = file ? URL.createObjectURL(file) : null;
    setPreviewImage(previewImageURL);
  };

  // Fetch email and username
  useEffect(() => {
    const authToken = Cookies.get("userToken");
    Axios.get(
      API+"/users/profile",
      {
        withCredentials: true,
        headers: {
          Authorization: `Bearer ${authToken}`,
        },
      }
    )
      .then((response) => {
        console.log("Profile response:", response.data);
        setUserData({
          username: response.data.username,
          email: response.data.email,
          profilePicture: response.data.profilePicture,
        });
        setProfilePictureURL(response.data.profilePicture)
      })
      .catch((error) => {
        console.error(error);
      });
  }, []);

  // Fetch and display saved ingredients
  const fetchSavedIngredients = useCallback(async () => {
    try {
      const response = await Axios.get(
        API+"/users/saved_ingredients",
        {
          withCredentials: true,
          headers: {
            Authorization: `Bearer ${Cookies.get("userToken")}`,
          },
        }
      );
      console.log("Response Data:", response.data);
      setSavedIngredients(response.data.savedIngredients);
      setIngredientName("");
      setIngredientQuantity("");
    } catch (error) {
      console.error(error);
    }
  }, []);

  const { username = "", email = "" } = userData || {};

  // insert ingredients into the fridge table
  const handleSaveIngredients = async () => {
    try {
      console.log("Input Ingredient Name:", ingredientName);

      const trimmedIngredientName = ingredientName.toLowerCase().trim();
      console.log("trimmedIngredientName:" + trimmedIngredientName);

      if (!trimmedIngredientName) {
        console.error("Ingredient name cannot be empty.");
        return;
      }

      if (!ingredientQuantity) {
        console.error("Quantity cannot be empty.");
        return;
      }

      const response = await Axios.post(
        API+"/users/profile_ingredient_list",
        {
          name: trimmedIngredientName,
          quantity: ingredientQuantity,
        },
        {
          withCredentials: true,
          headers: {
            Authorization: `Bearer ${Cookies.get("userToken")}`,
          },
        }
      );
      console.log(response.data);
      // Only fetch ingredients after a successful save
      fetchSavedIngredients();
      setIngredientName("");
      setIngredientQuantity("");
    } catch (error) {
      console.error(error);
    }
  };

  useEffect(() => {
    fetchSavedIngredients();
  }, []);

  const handleDeleteIngredient = async (ingredientName) => {
    try {
      const response = await Axios.delete(
        "http://localhost:3001/users/delete_ingredient",
        {
          withCredentials: true,
          headers: {
            Authorization: `Bearer ${Cookies.get("userToken")}`,
          },
          data: {
            name: ingredientName,
          },
        }
      );
      setSavedIngredients(response.data.savedIngredients);
    } catch (error) {
      console.error(error);
    }
  };

  const handleProfilePictureUpload = async () => {
    try {
      if (selectedFile) {
        const formData = new FormData();
        formData.append("profilePicture", selectedFile);

        const response = await Axios.post(
          API + "/users/upload_profile_picture",
          formData,
          {
            withCredentials: true,
            headers: {
              Authorization: `Bearer ${Cookies.get("userToken")}`,
              "Content-Type": "multipart/form-data",
            },
          }
        );

        // Update the user data with the new profile picture file name
        setUserData((prevUserData) => ({
          ...prevUserData,
          profilePicture: response.data.filePath.split('/').pop(),
        }));

        // Update the profile picture URL to trigger a re-render
        setProfilePictureURL(response.data.filePath.split('/').pop());

        setShowUploadModal(false);
      }
    } catch (error) {
      console.error(error);
    }
  };

  // console.log("Profile Picture File Name:", userData.profilePicture);

  return (
    <div>
      <Header />
      <div className="Profile-Card">
        <div className="Profile-Picture">
          <div className="picture-icon" onClick={() => setShowUploadModal(true)}> 
            <img 
              className="pic-icon" 
              src="./images/photo_icon.png" 
              alt="Add Photo"
            />
          </div>
          {userData.profilePicture ? ( 
            <img 
            className="profile-pic"
            src={`http://localhost:3000/uploads/${userData.profilePicture}`} 
            alt="Profile"
          />
          ) : (
            <img 
              className="default" 
              src="./images/profile_pic.jpg"
              alt="Default Profile"
            />
          )}
        </div>
        
        <div className="card-content">
          <h1>Profile</h1>
          <p>Username: {username}</p>
          <p>Email: {email}</p>
        </div>
      </div>
      {showUploadModal && (
        <div className="upload-modal">
          <div className="upload-modal-content">
            <input
              type="file"
              accept="image/jpg, image/png, image/jpeg"
              id="file"
              onChange={handleFileChange}
              style={{ display: 'none' }}
            />
            <label htmlFor="file" className="file-input-button">
              Change Profile Picture
            </label>
            {previewImage && (
              <img
                className="preview-image"
                src={previewImage}
                alt="Preview"
              />
            )}
            <button type="button" className="pic-upload" onClick={() => handleProfilePictureUpload()}>
              Upload Picture
            </button>
            <button type="button" className="pic-cancel" onClick={() => setShowUploadModal(false)}>
              Cancel
            </button>
          </div>
        </div>
      )}
      {/* <form>
        <h3>Ingredients:</h3>
        <input
          type="text"
          value={ingredientName}
          onChange={(e) => setIngredientName(e.target.value)}
          placeholder="Ingredient Name"
        />
        <input
          type="text"
          value={ingredientQuantity}
          onChange={(e) => setIngredientQuantity(e.target.value)}
          placeholder="Quantity"
        />
        <button type="button" onClick={handleSaveIngredients}>
          Save
        </button>
      </form> */}
      {/* <div>
        <h3>Saved Ingredients:</h3>
        <ul>
          {savedIngredients &&
            savedIngredients.map((ingredient, index) => (
              <li key={index}>
                {ingredient.name} - {ingredient.quantity}
                <button onClick={() => handleDeleteIngredient(ingredient.name)}>
                  Delete
                </button>
              </li>
            ))}
        </ul>
      </div> */}
      <Footer />
    </div>
  );
};

export default Profile;

2

Answers


  1. You can achieve this by doing the following:

    1. Extract the API method you use to fetch the user details from the useEffect into a function
    function getUserData(){
     const authToken = Cookies.get("userToken");
        Axios.get(
          API+"/users/profile",
          {
            withCredentials: true,
            headers: {
              Authorization: `Bearer ${authToken}`,
            },
          }
        )
          .then((response) => {
            console.log("Profile response:", response.data);
            setUserData({
              username: response.data.username,
              email: response.data.email,
              profilePicture: response.data.profilePicture,
            });
            setProfilePictureURL(response.data.profilePicture)
          })
          .catch((error) => {
            console.error(error);
          });
    
    }
    
    

    After which you can now call the function inside the useEffect

    useEffect(() => {
      getUserData();
    }, [])
    
    1. Next, in handleProfilePictureUpload call getUserData after the profile picture has been successfully uploaded.
     const handleProfilePictureUpload = async () => {
        try {
          if (selectedFile) {
            const formData = new FormData();
            formData.append("profilePicture", selectedFile);
    
            const response = await Axios.post(
              API + "/users/upload_profile_picture",
              formData,
              {
                withCredentials: true,
                headers: {
                  Authorization: `Bearer ${Cookies.get("userToken")}`,
                  "Content-Type": "multipart/form-data",
                },
              }
            );
    
            if(response){ // do a check to make sure the response is valid.
               getUserData();
               setShowUploadModal(false);
            }
            
          }
        } catch (error) {
          console.error(error);
        }
      };
    
    Login or Signup to reply.
  2. There is a possibility you issue is cache please replace

    src={`http://localhost:3000/uploads/${userData.profilePicture}`}
    

    with

    src={`http://localhost:3000/uploads/${userData.profilePicture}?${new Date().getTime()}`}
    

    if it works out then it is cache; you can now create a variable for ${new Date().getTime()} or use number that change in every time the component reload in other to keep a clean code

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search