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