toggle the value returned by a usecontext hook

Viewed 65

Working on a nextjs app using typescript w/ a firestore backend.

I currently am using my own useContext hook to make globally available a firestore user object, an array of 'business entity IDs' (strings) that the user is authorized to view, as well as an array of objects containing each biz's document data.

Because a user can be a member of multiple business entities, I'd like to be able to have them toggle between a 'current business' object whose document ID would be used to pass into further queries as well as document add / update functions.

I'm just not sure how to go about trigger the setting and switching of this 'current biz' id.

I'm assuming I'd want to somehow set the value in another usecontext hook so that the value is globally available to use around the app?

Below is my current custom context hooks doc

please let me know if you'd need to see any more data

import { auth, firestore } from "../lib/firebase";
import { useContext, useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import {
  doc,
  onSnapshot,
  collection,
  query,
  where,
  getDocs,
} from "firebase/firestore";
import { BizContext } from "./context";

//types

// Custom hook to read  auth record and user profile doc
export function useUserData() {
  const [user, loading, error] = useAuthState(auth);
  const [username, setUsername] = useState(null);

  useEffect(() => {
    // turn off realtime subscription
    let unsubscribe;

    //this actively listens to a the users collection
    if (user) {
      const userDocRef = doc(firestore, "users", user.uid);
      unsubscribe = onSnapshot(userDocRef, (doc) => {
        setUsername(doc.data()?.username);
      });
    } else {
      setUsername(null);
    }

    return unsubscribe;
  }, [user]);

  return { user, username };
}

export function useBelongsTo() {
  const [user] = useAuthState(auth);
  const [belongsTo, setBelongsTo] = useState<[]>([]);
  const [bizInfo, setBizInfo] = useState<[]>([]);

  useEffect(() => {
    let unsubscribe;

    if (user) {
      const bizColRef = collection(firestore, "businesses");
      const q = query(bizColRef, where("admins", "array-contains", user.uid));

      unsubscribe = onSnapshot(q, (querySnapshot) => {
        let bizId: [] = [];
        let bizInfo: [] = [];
        querySnapshot.forEach((doc) => {
          bizId.push(doc.id);
          bizInfo.push(doc.data());
        });
        setBelongsTo(bizId);
        setBizInfo(bizInfo);
      });
    } else {
      setBelongsTo([]);
      console.log("no businesses, no user");
    }
    return unsubscribe;
  }, [user]);

  return { belongsTo, bizInfo };
}


_app.tsx

import "../styles/globals.css";
import type { AppProps } from "next/app";
import { ChakraProvider } from "@chakra-ui/react";
import customTheme from "../extendTheme";
import { Toaster } from "react-hot-toast";
import { BizContext, UserContext } from "../lib/context";
import { useBelongsTo, useUserData } from "../lib/hooks";

import Layout from "../components/layout/Layout";

function MyApp({ Component, pageProps }: AppProps) {
  const userData = useUserData();
  const bizData = useBelongsTo();

  return (
    <ChakraProvider theme={customTheme}>
      <UserContext.Provider value={userData}>
        <BizContext.Provider value={bizData}>
          <Layout>
            <Component {...pageProps} />
            <Toaster />
          </Layout>
        </BizContext.Provider>
      </UserContext.Provider>
    </ChakraProvider>
  );
}

export default MyApp;

context.tsx

import React from "react";

export const UserContext = React.createContext({});
export const BizContext = React.createContext({});

1 Answers

If you want to make belongsTo and bizInfo globally for later usage, I recommend you to use react Context instead of using hooks. Move the logic to the context like the sample below.

import { useContext, createContext, useEffect, useState } from "react";
export const AuthContext = createContext();

export function AuthContextProvider({ children }) {
  const [user] = useAuthState(auth);
  const [belongsTo, setBelongsTo] = useState<[]>([]);
  const [bizInfo, setBizInfo] = useState<[]>([]);
  const [currentBizId, setCurrentBizId] = useState();

  useEffect(() => {
    let unsubscribe;

    if (user) {
      const bizColRef = collection(firestore, "businesses");
      const q = query(bizColRef, where("admins", "array-contains", user.uid));

      unsubscribe = onSnapshot(q, (querySnapshot) => {
        let bizId: [] = [];
        let bizInfo: [] = [];
        querySnapshot.forEach((doc) => {
          bizId.push(doc.id);
          bizInfo.push(doc.data());
        });
        setBelongsTo(bizId);
        setBizInfo(bizInfo);
      });
    } else {
      setBelongsTo([]);
      console.log("no businesses, no user");
    }
    return unsubscribe;
  }, [user]);

  function switchBusinessEntity(id){
    let currentBusinessEntity=belongsTo.find(biz=>biz.id === id);

    setCurrentBizId(currentBusinessEntity);
  }

  return <AuthContext.Provider
    value={{
      belongsTo,
      bizInfo,
      currentBizId,
      switchBusinessEntity
    }}>
    {children}
  </AuthContext.Provider>;
}

export const useAuthContext = () => useContext(AuthContext);

And in the _app.js, wrapper the App with AuthContextProvider

import { AuthContextProvider } from "@/contexts/authContext";

function MyApp({ Component, pageProps }) {
  return <AuthContextProvider>
    <Component {...pageProps} />
    </AuthContextProvider>;
}

export default MyApp;

And in the component, you can access the global states like this:

function YourComponent(){
    const {
     belongsTo,
     bizInfo,
     currentBizId,
     switchBusinessEntity
   } = useAuthContext()

   return <select onChange={e => switchBusinessEntity(e.target.value)>
  {belongsTo.map((bizId, index) => <option key={index} value={bizId.value}>{bizId}</option>)}
</select>
}
Related