Reactjs 如何将默认的react上下文值设置为来自firestore的数据?

Reactjs 如何将默认的react上下文值设置为来自firestore的数据?,reactjs,typescript,firebase,Reactjs,Typescript,Firebase,我正在构建一个“训练计划计划”应用程序,该训练计划在应用程序中使用SetProgram上下文进行处理,并使用名为useProgram的自定义挂钩进行更新。我需要的是,当用户登录时,该应用程序将从firestore获取数据并显示用户的训练计划,我如何才能做到这一点?请记住,useProgram挂钩在整个应用程序中也用于编辑和更新个人的训练计划 App.tsx import React,{useContext,useffect,useState}来自“React”; 从“react Router d

我正在构建一个“训练计划计划”应用程序,该训练计划在应用程序中使用SetProgram上下文进行处理,并使用名为useProgram的自定义挂钩进行更新。我需要的是,当用户登录时,该应用程序将从firestore获取数据并显示用户的训练计划,我如何才能做到这一点?请记住,useProgram挂钩在整个应用程序中也用于编辑和更新个人的训练计划

App.tsx

import React,{useContext,useffect,useState}来自“React”;
从“react Router dom”导入{BrowserRouter as Router};
从“/Router”导入AppRouter;
从“/firebase”导入FirebaseApp;
从“/context/program”导入SetProgram;
从“/components/hooks/useProgram”导入{useProgram}”;
从“/firebase/firebase”导入firebaseApp;
从“react firebase hooks/auth”导入{useAuthState};
函数App(){
const program=useProgram();
const day=useDay();
const[user,loading,error]=useAuthState(firebaseApp.auth);
返回(
);
}
导出默认应用程序;
firebase.ts

从“firebase/app”导入firebase;
导入“firebase/auth”;
导入“firebase/firestore”;
从“/config”导入firebaseConfig;
级火基{
auth:firebase.auth.auth;
用户:firebase.user | null |未定义;
db:firebase.firestore.firestore;
用户程序:{}|未定义;
构造函数(){
firebase.initializeApp(firebaseConfig);
this.auth=firebase.auth();
this.db=firebase.firestore();
}
异步寄存器(){
if(this.user){
this.db.collection(“users”).doc(this.user.uid).set({
名称:this.user.displayName,
电子邮件:this.user.email,
userId:this.user.uid,
程序:{},
});
}
}
异步getResults(){
return wait this.auth.getRedirectResult()。然后((结果)=>{
日志(“results.user”,results.user);
如果(!results.additionalUserInfo?.isNewUser){
这个.getProgram();
}否则{
这是register();
}
});
}
异步登录(
用户:firebase.user | null |未定义,
加载:布尔,
错误:firebase.auth.error |未定义
) {
const provider=new firebase.auth.GoogleAuthProvider();
返回等待this.auth
.signInWithRedirect(提供程序)
.然后(()=>this.getResults());
}
异步注销(){
return等待this.auth.signOut(),然后(()=>console.log(“注销”);
}
异步更新程序(用户:firebase.user,程序:{}){
if(this.userProgram!==程序){
firebaseApp.db
.收集(“用户”)
.doc(user.uid)
.更新({
节目:节目,,
})
。然后(()=>console.log(“程序更新成功!”)
.catch((错误:any)=>console.error(“错误更新程序:”,错误));
}否则{
log(“程序没有更改!”);
}
}
异步getProgram(){
firebaseApp.db
.收集(“用户”)
.doc(此.user?.uid)
.get()
。然后((doc)=>{
console.log(“你好”);
如果(文件存在){
this.userProgram=doc.data()?.program;
log(“this.userProgram”,this.userProgram);
}否则{
log(“doc.data()”,doc.data());
}
});
}
}
const firebaseApp=新的Firebase();
导出默认firebaseApp;
programContext.tsx

从“React”导入React;
从“./interfaces/Program”导入程序,{muscleGroup,DefaultProgram};
导出接口程序上下文{
程序:程序|未定义;
天数:数组|未定义;
setProgram:(p:程序)=>无效;
}
导出常量DefaultProgramContext:ProgramContextInt={
程序:未定义,
天数:未定义,
setProgram:(p:Program):void=>{},
};
const ProgramContext=React.createContext(
DefaultProgramContext
);
导出默认程序上下文;
useProgram.tsx


从“React”导入React;
进口{
ProgramContextInt,
默认程序上下文,
}来自“../../context/program”;
从“../../interfaces/Program”导入程序,{muscleGroup};
从“react firebase hooks/auth”导入{useAuthState};
从“../../firebase”导入firebaseApp;
export const useProgram=():ProgramContextInt=>{
const[user]=useAuthState(firebaseApp.auth);
const[program,setEditedProgram]=React.useState();
const[days,setProgramDays]=React.useState<
[string,muscleGroup][]|未定义
>(程序和对象条目(程序));
const setProgram=React.useCallback(
(程序:程序):void=>{
firebaseApp.updateProgram(用户、程序);
设置编辑程序(程序);
setProgramDays(Object.entries(program));
},
[用户]
);
返回{
节目,,
天,
setProgram,
};
};

我认为有两种方法可以解决这个问题:

  • 更新
    ProgramContext
    以确保用户已登录
  • 应用程序
    或您需要确保用户已登录的任何其他入口点包装在单独的
    UserContextProvider中
  • 让我们来讨论后一种方法,我们可以将其包装在一个名为
    UserContext
    的单独上下文中。Firebase为我们提供了一个名为
    onAuthStateChanged
    的侦听器,我们可以在上下文中使用它,如下所示:

    import { createContext, useEffect, useState } from "react";
    import fb from "services/firebase"; // you need to define this yourself. It's just getting the firebase instance. that's all
    import fbHelper from "services/firebase/helpers"; // update path based on your project organization
    
    type FirestoreDocSnapshot = firebase.default.firestore.DocumentSnapshot<firebase.default.firestore.DocumentData>;
    
    const UserContext = createContext({ user: null, loading: true });
    
    const UserContextProvider = ({ children }) => {
      const [user, setUser] = useState(null);
      const [loading, setLoading] = useState(true);
      const userContext = { user, loading };
    
      const updateUser = (snapShot: FirestoreDocSnapshot) => {
        setUser({
          id: snapShot.id,
          ...snapShot.data,
        });
      };
    
      const authStateListener = async (authUser: firebase.default.User) => {
        try {
          if (!authUser) {
            setUser(authUser);
            return;
          }
    
          const fbUserRef = await fbHelper.findOrCreateFirestoreUser(authUser)
    
          if ("error" in fbUserRef) throw new Error(fbUserRef?.error);
    
          (fbUserRef as FirestoreUserRef).onSnapshot(updateUser)
        } catch (error) {
          throw error;
        } finally {
          setLoading(false);
        }
      };
    
      useEffect(() => {
        const unSubscribeAuthStateListener = fb.auth.onAuthStateChanged(authStateListener);
    
        return () => unSubscribeAuthStateListener();
      }, [])
    
      return (
        <UserContext.Provider value={userContext}>
          {children}
        </UserContext.Provider>
      )
    };
    
    export default UserContextProvider;
    
    从“react”导入{createContext,useffect,useState};
    从“服务/firebase”导入fb;//你需要自己定义这个。它只是获取firebase实例。这就是全部
    从“服务/firebase/helpers”导入fbHelper;//根据项目组织更新路径
    输入FirestoreDocSnapshot=firebase.default.firestore.DocumentSnapshot;
    const UserContext=createContext({user:null,load:true});
    const UserContextProvider=({children})=>{
    const[user,setUser]=useState(null);
    const[loading,setLoading]=useState(true);
    常量用户上下文=
    
    export type FirestoreUserRef = firebase.default.firestore.DocumentReference<firebase.default.firestore.DocumentData>
    
    const findOrCreateFirestoreUser = async (authUser: firebase.default.User, additionalData = {}): Promise<FirestoreUserRef | { error?: string }> => {
      try {
        if (!authUser) return { error: 'authUser is missing!' };
    
        const user = fb.firestore.doc(`users/${authUser.uid}`); // update this logic according to your schema
        const snapShot = await user.get();
    
        if (snapShot.exists) return user;
    
        const { email } = authUser;
    
        await user.set({
          email,
          ...additionalData
        });
    
        return user;
      } catch (error) {
        throw error;
      }
    };