Skip to content

Multiple function calls when using custom hook useCallableFunctionResponseΒ #606

@VBota1

Description

@VBota1

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

  • Run app (possible with npm run dev)
  • Open app in browser
  • In Firebase console -> Functions select your_callable_function and open the Detailed usage stats using the 3 dot menu.
    This will open the Google Cloud Console -> Function details page for the function.
  • Select the Logs tab and wait for the logs to load.
  • Check how many times the function was called when you refreshed the browser page.

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>
      );
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions