【问题标题】:How to set the default react context value as data from firestore?如何将默认反应上下文值设置为来自 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,
  };
};

【问题讨论】:

    标签: reactjs typescript firebase


    【解决方案1】:

    我认为有两种方法可以处理:

    1. 更新ProgramContext 以确保用户已登录
    2. 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 中。因此,每当您登录或注销时,都会调用这个特定的侦听器。

    【讨论】:

      猜你喜欢
      • 2012-03-10
      • 1970-01-01
      • 2020-06-24
      • 1970-01-01
      • 2021-09-27
      • 1970-01-01
      • 2014-06-01
      • 2020-01-01
      • 1970-01-01
      相关资源
      最近更新 更多