如何将默认反应上下文值设置为来自 Firestore 的数据?
Posted
技术标签:
【中文标题】如何将默认反应上下文值设置为来自 Firestore 的数据?【英文标题】:How to set the default react context value as data from firestore? 【发布时间】:2021-06-26 01:55:52 【问题描述】:我正在构建一个锻炼计划计划器应用程序,锻炼计划在应用程序中使用 SetProgram 上下文进行处理,并使用名为 useProgram 的自定义挂钩进行更新。我需要当用户登录该应用程序将从 Firestore 获取数据并显示用户的锻炼计划时,我该怎么做?请记住,useProgram 挂钩也用于整个应用程序来编辑和更新一个人的锻炼计划。
App.tsx
import React, useContext, useEffect, useState from "react";
import BrowserRouter as Router from "react-router-dom";
import AppRouter from "./Router";
import FirebaseApp from "./firebase";
import SetProgram from "./context/program";
import useProgram from "./components/hooks/useProgram";
import firebaseApp from "./firebase/firebase";
import useAuthState from "react-firebase-hooks/auth";
function App()
const program = useProgram();
const day = useDay();
const [user, loading, error] = useAuthState(firebaseApp.auth);
return (
<div className="App">
<SetProgram.Provider value=program>
<Router>
<AppRouter />
</Router>
</SetProgram.Provider>
</div>
);
export default App;
firebase.ts
import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
import firebaseConfig from "./config";
class Firebase
auth: firebase.auth.Auth;
user: firebase.User | null | undefined;
db: firebase.firestore.Firestore;
userProgram: | undefined;
constructor()
firebase.initializeApp(firebaseConfig);
this.auth = firebase.auth();
this.db = firebase.firestore();
async register()
if (this.user)
this.db.collection("users").doc(this.user.uid).set(
name: this.user.displayName,
email: this.user.email,
userId: this.user.uid,
program: ,
);
async getResults()
return await this.auth.getRedirectResult().then((results) =>
console.log("results.user", results.user);
if (!results.additionalUserInfo?.isNewUser)
this.getProgram();
else
this.register();
);
async login(
user: firebase.User | null | undefined,
loading: boolean,
error: firebase.auth.Error | undefined
)
const provider = new firebase.auth.GoogleAuthProvider();
return await this.auth
.signInWithRedirect(provider)
.then(() => this.getResults());
async logout()
return await this.auth.signOut().then(() => console.log("logged out"));
async updateProgram(user: firebase.User, program: )
if (this.userProgram !== program)
firebaseApp.db
.collection("users")
.doc(user.uid)
.update(
program: program,
)
.then(() => console.log("Program updated successfully!"))
.catch((error: any) => console.error("Error updating program:", error));
else
console.log("No changes to the program!");
async getProgram()
firebaseApp.db
.collection("users")
.doc(this.user?.uid)
.get()
.then((doc) =>
console.log("hello");
if (doc.exists)
this.userProgram = doc.data()?.program;
console.log("this.userProgram", this.userProgram);
else
console.log("doc.data()", doc.data());
);
const firebaseApp = new Firebase();
export default firebaseApp;
programContext.tsx
import React from "react";
import Program, muscleGroup, DefaultProgram from "../interfaces/program";
export interface ProgramContextInt
program: Program | undefined;
days: Array<[string, muscleGroup]> | undefined;
setProgram: (p: Program) => void;
export const DefaultProgramContext: ProgramContextInt =
program: undefined,
days: undefined,
setProgram: (p: Program): void => ,
;
const ProgramContext = React.createContext<ProgramContextInt>(
DefaultProgramContext
);
export default ProgramContext;
useProgram.tsx
import React from "react";
import
ProgramContextInt,
DefaultProgramContext,
from "../../context/program";
import Program, muscleGroup from "../../interfaces/program";
import useAuthState from "react-firebase-hooks/auth";
import firebaseApp from "../../firebase";
export const useProgram = (): ProgramContextInt =>
const [user] = useAuthState(firebaseApp.auth);
const [program, setEditedProgram] = React.useState<Program | undefined>();
const [days, setProgramDays] = React.useState<
[string, muscleGroup][] | undefined
>(program && Object.entries(program));
const setProgram = React.useCallback(
(program: Program): void =>
firebaseApp.updateProgram(user, program);
setEditedProgram(program);
setProgramDays(Object.entries(program));
,
[user]
);
return
program,
days,
setProgram,
;
;
【问题讨论】:
【参考方案1】:我认为有两种方法可以处理:
-
更新
ProgramContext
以确保用户已登录
将App
或任何其他您需要确保用户已登录的入口点包装在单独的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;
帮助器可以是这样的:
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;
;
然后将提供firestore
数据的其他上下文包装在此UserContextProvider
中。因此,每当您登录或注销时,都会调用这个特定的侦听器。
【讨论】:
以上是关于如何将默认反应上下文值设置为来自 Firestore 的数据?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 fetch() 函数的结果传递给 React 上下文值,而不是我为钩子设置的默认值?
Django ChoiceArrayField:如何设置默认值