React Context上下文状态设置位置及调用setUserProfileContext时报错的问题求助
React Context上下文状态设置位置及调用setUserProfileContext时报错的问题求助
我开发了一个在Firebase中存储用户信息的应用,主个人资料页通过一个钩子从Firebase/Storage获取数据。现在我想在这个页面里从钩子拿到个人资料数据,然后把它设置到实际的Context中——虽然我不确定这是不是正确的做法,但这是ChatGPT给的方案。
下面是我的ProfileForm组件代码:
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)} /> </> ); };
个人资料页还有一个编辑模态框,用来修改资料并存储到后端:
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); } };
这是我的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; };
当我尝试在钩子中设置Context时,出现了以下错误:
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, }; };
备注:内容来源于stack exchange,提问作者D.Hodges




