FirebaseExtended / reactfire

Hooks, Context Providers, and Components that make it easy to interact with Firebase.
https://firebaseopensource.com/projects/firebaseextended/reactfire/
MIT License
3.5k stars 400 forks source link

Multiple function calls when using custom hook useCallableFunctionResponse #606

Open VBota1 opened 5 months ago

VBota1 commented 5 months ago

Short description

Using the useCallableFunctionResponse hook from reactfire causes the function to be called twice. This might lead to undesired costs.

Version info

React: "^18" Firebase: "^10.0.0" ReactFire: "^4.2.3" Next: "14.1.0" Node: "^20"

Test case

Content of app/layout.tsx:

    "use client"

    import { Inter } from "next/font/google";
    import "./globals.css";
    import {FirebaseAppProvider} from "reactfire";

    const inter = Inter({ subsets: ["latin"] });

    const firebaseConfig = {
      // your firebase config
    };

    export default function RootLayout({
      children,
    }: Readonly<{
      children: React.ReactNode;
    }>) {
      return (
        <html lang="en">
          <FirebaseAppProvider firebaseConfig={firebaseConfig}>
            <body className={inter.className}>{children}</body>
          </FirebaseAppProvider>
        </html>
      );
    }

Content of app/page.tsx:

    "use client"

    import {FunctionsProvider, useCallableFunctionResponse, useFirebaseApp} from "reactfire";
    import {getFunctions} from "@firebase/functions";
    import Test from "@/app/test";

    const FUNCTIONS_REGION = 'your region';

    export default function Home() {
      const firebaseApp = useFirebaseApp(); 
      const functions = getFunctions(firebaseApp, FUNCTIONS_REGION);

      return (
        <FunctionsProvider sdk={functions}>
            <main className="flex min-h-screen flex-col items-center justify-between p-24">
              <Test /> 
            </main>
        </FunctionsProvider>
      );
    }

Content of app/test.tsx:

    import {useCallableFunctionResponse} from "reactfire";

    export default function Test() {
      const responseObservable = useCallableFunctionResponse<any, string>('your_callable_function', {data: {}});

      return (
        <div>
          {
            responseObservable.status === 'loading' ?
              <p>Loading ...</p> :
              <p>{responseObservable.data}</p>
          }
        </div>
      );
    }

Steps to reproduce

Expected behavior

The function should be called once.

202x-xx-xx xx:xx:xx.731 xxx | POST 200 xxx B x ms xxxxxx  https://region-project.cloudfunctions.net/your_callable_function 

Actual behavior

The function is actually called twice.

202x-xx-xx xx:xx:xx.731 xxx | POST 200 xxx B x ms xxxxxx  https://region-project.cloudfunctions.net/your_callable_function
202x-xx-xx xx:xx:xx.798 xxx | POST 200 xxx B x ms xxxxxx  https://region-project.cloudfunctions.net/your_callable_function  

Workaround

Using the httpsCallable from firebase/functions combined with the native hooks useEffect and useState solves the issue.

Content of app/test.tsx:

    import {useFunctions} from "reactfire";
    import {httpsCallable} from "firebase/functions";
    import {useEffect, useState} from "react";

    export default function Test() {
      const functions = useFunctions();
      const dataReader = httpsCallable<object, string>(functions, 'your_callable_function');

      const [responseObservable, setResponseObservable] =
        useState<{status: string, data: string}>({status: 'loading', data: ''});

      useEffect(() => {
        dataReader({})
          .then((result) => {
            setResponseObservable({status: 'success', data: result.data});
          })
          .catch((error) => {
            setResponseObservable({status: 'error', data: error});
          });
      }, []);

      return (
        <div>
          {
            responseObservable.status === 'loading' ?
              <p>Loading ...</p> :
              <p>{responseObservable.data}</p>
          }
        </div>
      );
    }
JCBsystem commented 4 months ago

try google react useeffect called twice