0

I've created an app that stores user information in firebase.

The main profile page consumes the data via a hook that retrieves the data from firebase/storage. What I'm trying to do in this page is get the profile data from the hook and set the profile data in the actual context. I realize this may not be the right now but it's what's been prescribed by ChatGPT.

export const ProfileForm = () => {
  const [profile, setProfile] = useState({
    username: "",
    bio: "",
    gender: "",
    sexo: "",
    edu: "",
    drinking: "",
    smoking: "",
    dob: "",
  });
  const [showLogin, setShowLogin] = useState(false);
  const { userProfile, setUserProfile } = useGetUserProfile();
  const { userProfileContext, setUserProfileContext } = useUserProfile()

  useEffect(() => {
    if (userProfile) {
      setProfile(userProfileContext);
    }
  }, [userProfile, setUserProfile]);

  const { showAlert } = useContext(AlertContext);

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)} noValidate>
        <Box display="flex" justifyContent="flex-end" p={2}>
          <Button
            variant="contained"
            color="secondary"
            onClick={async () => {
              await signOut(auth);
              window.location.href = "/login";
            }}
            sx={{
              borderRadius: "50%",
              width: 80,
              height: 80,
            }}>
            <span role="img" aria-label="logout">
              Logout
            </span>
          </Button>
        </Box>
        <Box mt={0} display="flex" justifyContent="center">
          <img
            src={profilePlaceholder}
            alt="Profile Placeholder"
            style={{ maxWidth: "100%", height: "auto" }}
          />
          <Box sx={{ flexDirection: "column" }}>
            <Typography
              ml={2}
              fontSize={{ base: "sm", md: "lg" }}
              color="white"
              sx={{ fontSize: "2rem" }}>
              {profile?.username || ""}
            </Typography>
            <Button
              variant="contained"
              size="small"
              onClick={() => setShowLogin(true)}
              sx={{ marginTop: 2, background: "white", color: "black" }}>
              Edit Profile
            </Button>
          </Box>
        </Box>
        <Card
          variant="outlined"
          sx={{
            display: "flex",
            flexDirection: "column",
            gap: 2,
            p: 2,
            maxWidth: 300,
            margin: "20px auto 20px auto",
          }}>
          <FormControl>
            <TextField
              id="bio"
              label="User Bio"
              multiline
              autoFocus
              rows={4}
              value={profile?.bio || ""}
              name="bio"
              placeholder="Tell us about yourself"
              InputProps={{
                readOnly: true,
              }}
            />
          </FormControl>
          <FormControl required>
            <TextField
              label="*Date of birth"
              type="date"
              id="dob"
              name="dob"
              value={
                profile?.dob
                  ? new Date(profile?.dob).toISOString().split("T")[0]
                  : new Date().toISOString().split("T")[0]
              }
              InputProps={{
                readOnly: true,
              }}
            />
          </FormControl>
          <FormControl>
            <TextField
              id="gender"
              value={profile?.gender || ""}
              required
              fullWidth
              name="gender"
              label="Gender"
              InputProps={{
                readOnly: true,
              }}></TextField>
          </FormControl>
          <FormControl>
            <TextField
              id="sexo"
              value={profile?.sexo || ""}
              required
              fullWidth
              name="sexo"
              label="Sexual Orientation"
              InputProps={{
                readOnly: true,
              }}></TextField>
          </FormControl>
          <FormControl>
            <TextField
              id="edu"
              value={profile?.edu || ""}
              required
              label="Education"
              name="edu"
              InputProps={{
                readOnly: true,
              }}></TextField>
          </FormControl>
          <FormControl>
            <TextField
              id="drinking"
              required
              value={profile?.drinking || ""}
              label="Drinking"
              name="drinking"
              InputProps={{
                readOnly: true,
              }}></TextField>
          </FormControl>
          <FormControl>
            <TextField
              id="smoking"
              required
              value={profile?.smoking || ""}
              label="Smoking"
              name="smoking"
              InputProps={{
                readOnly: true,
              }}></TextField>
          </FormControl>
        </Card>
      </form>
      <EditProfileModal show={showLogin} close={() => setShowLogin(false)} />
    </>
  );
};

The profile page has an edit modal that edits the profile data and stores it in the backend.

MODAL

export const EditProfileModal = (props: any) => {
  const { userRef, setUserDbData } = usePostUserProfileToDb();
  const { userStorageData, setUserStorageData } = usePostUserProfileToStorage();
  const { userProfile, setUserProfile } = useGetUserProfile();

  const [profile, setProfile] = useState({
    username: "",
    bio: "",
    gender: "",
    sexo: "",
    edu: "",
    drinking: "",
    smoking: "",
    dob: "",
  });

  useEffect(() => {
    if (userProfile) {
      setProfile(userProfile);
    }
  }, [userProfile, setUserProfile]);

  
  const handleSubmit = async (e: any) => {
    e.preventDefault();
    try {
      setUserStorageData(profile);
      setUserDbData(profile);
      props.close();
    } finally {
      setIsSubmitting(false);
    }
  };

And here is the context.

import React, { createContext, useState, useContext } from 'react';

const UserProfileContext = createContext(null);


export const UserProfileProvider = ({ children }) => {
  const [userProfileContext, setUserProfileContext] = useState(null);

  return (
    <UserProfileContext.Provider value={{ userProfile: userProfileContext, setUserProfile: setUserProfileContext }}>
      {children}
    </UserProfileContext.Provider>
  );
};

export const useUserProfile = () => {
  const context = useContext(UserProfileContext);
  if (!context) {
    throw new Error('useUserProfile must be used within a UserProfileProvider');
  }
  return context;
};

When I try to set the context in the hook I get the following error:

This expression is not callable.
  Type 'never' has no call signatures.
import { useEffect, useState } from "react";
import useGetUserId from "./useGetUserId";
import { app } from "../environments/environment";
import { doc, getDoc, getFirestore } from "firebase/firestore";
import { useUserProfile } from "../Context/UserProfileContext";

const useGetUserProfile = () => {
  type profile = {
    email: string;
    username: string;
    userBio: string;
    dob: Date;
    gender: string;
    sexo: string;
    education: string;
    drinkingHabits: string;
    smokingHabits: string;
  };

  const db = getFirestore(app);
  const userId: string | null = useGetUserId();
  const [isLoading, setIsLoading] = useState(true);
  const [userProfile, setUserProfile] = useState<any | null>(null);
  const { userProfileContext, setUserProfileContext } = useUserProfile()

  useEffect(() => {
    const userProfile = async () => {
      setIsLoading(true);
      try {
        const userRef = localStorage.getItem("PROFILE_INFO");
        if (userRef) {
          const profile: profile = JSON.parse(userRef);
          setUserProfile(profile);
          setUserProfileContext(profile)
        } else {
          if (userId) {
            const id = JSON.parse(userId);
            const userRef = await getDoc(doc(db, "users", id.user.uid));
            if (userRef.exists()) {
              const profile = userRef.data();
              setUserProfile(profile);
            }
          }
        }
      } catch (error) {
        console.log("error", error);
      } finally {
        setIsLoading(false);
      }
    };
    userProfile();
  }, [setUserProfile]);
  return {
    isLoading,
    userProfile, setUserProfile
  };
};

export default useGetUserProfile;

So I'm not sure what I'm doing wrong. Where exactly does the context value get set, only in the children? From the context children, do you retrieve the profile data from the hooks and then set it, or do you set the context data in the hooks?

When i try to set the context state variable within the hook I get the error in the pic below: That error is in the actual hook that's getting the user data from the backend. My thought is I updated the context state variable within the hook enter image description here

15
  • Are you asking about your code, or some code that ChatGPT hallucinated? Can you edit to include the complete error message, any stacktrace if available, and indicate what specific line of code has the issue? Commented Feb 7 at 18:33
  • @DrewReese I'm asking about my code. I'm not getting any errors, it's just not doing what I expect it to do and I don't think I've implemented the context. correctly. I'm not 100% sure how to even word this request. I'll give it a shot. It sort of like, You don't know what you don't know Commented Feb 7 at 18:40
  • No worries, this is a collaborative effort. Let's start with the error message. What expression specifically is not callable? Commented Feb 7 at 18:48
  • @DrewReese I may not even be doing this correctly. So I was trying to set the context state in the actual hook with the set variable here: setUserProfileContext(profile) I was doing that in the useEffect of my hook. Commented Feb 7 at 19:09
  • Affirmative, the hook provides a setUserProfile function because this is the context value provided: value={{ userProfile: userProfileContext, setUserProfile: setUserProfileContext }}... The context is providing that function under a different name. You should use const { userProfile, setUserProfile } = useUserProfile() instead. Commented Feb 7 at 19:13

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.