Michael Stubbs
Michael Stubbs

Reputation: 37

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. - useEffect()

I get this error when I try and call a function I have imported within my useEffect() hook in Dashboard.jsx. I am just trying to pull in data from database on the page load pretty much so that when user click button they can send off correct credentials to the api.

I am pulling it in from database for security reasons, so client id is not baked into the code.

I am pretty sure that I am getting this error maybe because the function is not inside a react component? although I am not 100% sure. And if that is the case I am not sure of the best way to restructure my code and get the desired output.

Code below.

mavenlinkCredentials.js

import { doc, getDoc } from "firebase/firestore";
import { useContext } from "react";
import { AppContext } from "../../context/context";
import { db } from "../../firebase";

const GetMavenlinkClientId = async () => {
    const {setMavenlinkClientId} = useContext(AppContext)
    const mavenlinkRef = doc(db, 'mavenlink', 'application_id');
    const mavenlinkDocSnap = await getDoc(mavenlinkRef)
    
    if(mavenlinkDocSnap.exists()){
        console.log("mavenlink id: ", mavenlinkDocSnap.data());
        console.log(mavenlinkDocSnap.data()['mavenlinkAccessToken'])
        setMavenlinkClientId(mavenlinkDocSnap.data()['application_id'])
       
    } else {
        console.log("No doc");
    }
}

export default GetMavenlinkClientId;

Dashboard.jsx

import React, { useContext, useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { useNavigate } from "react-router-dom";
import { query, collection, getDocs, where, setDoc, doc, getDoc } from "firebase/firestore";
import { auth, db, logout } from "../firebase";
import { Button, Container, Grid, Paper } from "@mui/material";
import ListDividers from "../components/ListDividers";
import { AppContext } from "../context/context";
import axios from "axios";
import {SuccessSnackbar, ErrorSnackbar} from '../components/PopupSnackbar';
import GetMavenlinkClientId from "../helpers/firebase/mavenlinkCredentials";

const Dashboard = () => {
    const [user, loading, error] = useAuthState(auth);
    const [name, setName] = useState("");
    const [ accessToken, setAccessToken ] = useState("")
    const [errorAlert, setErrorAlert] = useState(false);
    const [successAlert, setSuccessAlert] = useState(false);
    const [mavenlinkClientId, setMavenlinkClientId] = useState("");
    const {isAuthenticated} = useContext(AppContext);
    const navigate = useNavigate();

    const uid = user.uid

    const parsedUrl = new URL(window.location.href)
    const userTokenCode = parsedUrl.searchParams.get("code");

    const { mavenlinkConnected, setMavenlinkConnected } =   useContext(AppContext)
    const { maconomyConnected,  setMaconomyConnected }  =   useContext(AppContext)
    const { bambooConnected,    setBambooConnected }    =   useContext(AppContext)

    const fetchUserName = async () => {
        try {
          const q = query(collection(db, "users"), where("uid", "==", user?.uid));
          const doc = await getDocs(q);
          const data = doc.docs[0].data();
          setName(data.name);
        } catch (err) {
          console.error(err);
          alert("An error occured while fetching user data");
        }
    };


    //
    useEffect(() => {
        if (loading) return;
        if (!user) return navigate("/");
        fetchUserName();

        if(userTokenCode !== null){
            authorizeMavenlink();
        }

        if(isAuthenticated){
            GetMavenlinkClientId()
        }
        
    }, [user, loading]);

    ///put this into a page load (use effect maybe) so user does not need to press button to connect to apis
    const authorizeMavenlink = () => {
        console.log(uid);
        const userRef = doc(db, 'users', uid);

        axios({
            //swap out localhost and store in variable like apitool
            method: 'post',
            url: 'http://localhost:5000/oauth/mavenlink?code='+userTokenCode,
            data: {}
        })
        .then((response) => {
            setAccessToken(response.data);
            setDoc(userRef, { mavenlinkAccessToken: response.data}, { merge: true });
            setMavenlinkConnected(true);
            setSuccessAlert(true);
        })
        .catch((error) => {
            console.log(error);
            setErrorAlert(true)
        });
    }
    //abstract out client id and pull in from db
    const getMavenlinkAuthorization = () => {
        window.open('https://app.mavenlink.com/oauth/authorize?client_id='+mavenlinkClientId+'&response_type=code&redirect_uri=http://localhost:3000');
        window.close();
    
    }

    const authorizeBamboo = () => {
        axios({
            method: 'get',
            url: 'http://localhost:5000/oauth/bamboo',
            data: {}
        })
        .then((response) => {
            console.log(response)

        })
        .catch((error) => {
            console.log(error);
        });
        // console.log('bamboo connected')
        setBambooConnected(true);
    }

    const authorizeMaconomy = () => {
        console.log("Maconomy connected")
        setMaconomyConnected(true);
    }

    const syncAccount = async() => {
        if(!mavenlinkConnected){
            await getMavenlinkAuthorization()
        }
        if (!bambooConnected){
            await authorizeBamboo();
        }
        if (!maconomyConnected){
            await authorizeMaconomy();
        }
    }

    const handleAlertClose = (event, reason) => {
        if (reason === 'clickaway') {
          return;
        }
        setSuccessAlert(false) && setErrorAlert(false);
    };

    console.log(mavenlinkClientId);
    
    return(
            <>
                <Container>
                    <div className="dashboard">
                        <h1>Dashboard</h1>
                        <Grid container spacing={2}>
                            <Grid item xs={12}>
                                <Paper style={{paddingLeft: "120px", paddingRight: "120px"}} elevation={1}>
                                    <div className="dashboard-welcome">
                                        <h2>Welcome {name}</h2>
                                        <h4>{user?.email}</h4>
                                        <hr/>
                                        <h2>Integrations</h2>
                                        <Button onClick={syncAccount}>
                                            Sync Account
                                        </Button>
                                        {/* <Button onClick={getMavenlinkClientId}>
                                            Bamboo Test
                                        </Button> */}
                                        <ListDividers/>
                                    </div>
                                </Paper>
                            </Grid>
                        </Grid>
                    </div>
                    {successAlert === true ? <SuccessSnackbar open={successAlert} handleClose={handleAlertClose}/> : <></> }
                    {errorAlert === true ? <ErrorSnackbar open={errorAlert} handleClose={handleAlertClose}/> : <></> }
                </Container>
            </>
    );
}

export default Dashboard;

Upvotes: 0

Views: 277

Answers (1)

Sareh
Sareh

Reputation: 19

the error is because you’re calling const {setMavenlinkClientId} = useContext(AppContext) inside the file mavenlinkCredentials.js which is not a react components. you could maybe change the function inside mavenlinkCredentials.js to accept a setMavenlinkClientId and pass it from outside like this.

  const GetMavenlinkClientId = async (setMavenlinkClientId) => {
    const mavenlinkRef = doc(db, 'mavenlink', 'application_id');
    const mavenlinkDocSnap = await getDoc(mavenlinkRef)
    
    if(mavenlinkDocSnap.exists()){
        console.log("mavenlink id: ", mavenlinkDocSnap.data());
        console.log(mavenlinkDocSnap.data()['mavenlinkAccessToken'])
        setMavenlinkClientId(mavenlinkDocSnap.data()['application_id'])
       
    } else {
        console.log("No doc");
    }
}

and then you can call this function in your dashboard.js like so,

   const {setMavenlinkClientId} = useContext(AppContext)
if(isAuthenticated){
        GetMavenlinkClientId(setMavenlinkClientId)
    }

Upvotes: 1

Related Questions