Uncaught (in promise) TypeError: Cannot read property 'httpsCallable' of undefined (Firebase functions issue)

Uncaught (in promise) TypeError: Cannot read property 'httpsCallable' of undefined (Firebase functions issue)

我正在查看文档,但看不出哪里出错了。有人可以阐明一下吗?我认为我没有正确引用 functions() 或 firebase。谢谢!这是所有代码:

firebase.js

import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'
import 'firebase/functions'
const dev = true


export const app = !firebase.apps.length ? firebase.initializeApp({
  apiKey: "AIzaSyBDrj-rL-Mzswu9VXhgp-RuvP9Hvl1kqQ0",
  authDomain: "edit-elements.firebaseapp.com",
  databaseURL: "https://edit-elements-default-rtdb.firebaseio.com",
  projectId: "edit-elements",
  storageBucket: "edit-elements.appspot.com",
  messagingSenderId: "340652433701",
  appId: "1:340652433701:web:a26472592c1538bbac7acc",
  measurementId: "G-945XC7348K"
}) : firebase.app()

export const auth = app.auth()
export const db = app.firestore()
export const functions = dev ? app.functions().useEmulator("localhost", '5001') : app.functions()

checkout.js(调用函数createFreeOrder的地方)

import PayPalComponent from '../components/PayPalComponent'
import { Button, Grid, TextField, Typography } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import { useAuth } from '../contexts/AuthContext'
import { useCart } from '../contexts/CartContext'
import Cart from '../components/Cart'
import LoginForm from '../components/LoginForm'
import SignupForm from '../components/SignupForm'
import { useEffect, useState } from 'react'
import { client } from '../prismic-configuration'
import { Actions } from '../utils/cartActions'
import { db, functions } from '../firebase'

const theme = makeStyles({
  checkoutContainer: {
    margin: 'auto',
    maxWidth: '1200px',
    paddingTop: '2rem',

    '&: section': {
      minHeight: '100vh'
    },

    couponInput: {
      '& fieldset': {
        borderRadius: '6px 0px 0px 6px'
      }
    }
  }
})

export default function Checkout() {
  const { currentUser } = useAuth()
  const { cart, cartTotal, dispatch } = useCart()
  const styles = theme()
  const [showLogin, setShowLogin] = useState(false)
  const hasItems = Boolean(Object.keys(cart.products).length)
  const [couponCode, setCouponCode] = useState('')
  const [couponError, setCouponError] = useState('')
  const cartCost = cartTotal()
  console.log(functions, db)


  const checkCoupon = async () => {
    setCouponError('')
    if (couponCode == '') return null
    console.log('NOTICE: Please note that the coupons are checked server-side, so any attempt to manipulate them here will do nothing, and you *will* be charged the price without a valid coupon')
    await client.getByUID('coupon', couponCode.toLowerCase())
    .then(res => {
      if (res.data) dispatch({ type: Actions.ADD_COUPON, payload: res })
    })
    .catch(err => setCouponError('No Coupon / Expired Coupon'))
  }

  const handleFreeOrder = async () => {
    const createFreeOrder = functions.httpsCallable('createFreeOrder')
    createFreeOrder(cart.products)
  }

  return (
    <Grid container direction="column" className={styles.checkoutContainer}>
      <Grid item>
        <Typography variant="h1" mb={'2rem'}>Shopping Cart</Typography>
      </Grid>
      { !currentUser &&
        <>
          <Grid item p={'.5rem'}>
            <Typography variant="subtitle1">Please note that accounts are required for purchase, as it allows us to securely generate download tokens and process invoices.</Typography>
          </Grid>
          { !showLogin ? 
          <Grid item>
            <SignupForm dontRedirect/>
            <Typography>Already have an account? <span style={{cursor: 'pointer'}} onClick={prev => setShowLogin(true)}>Click here</span> to login.</Typography>
          </Grid>
          :
          <Grid item>
            <LoginForm />
            <Typography>Don't have an account? <span style={{color: '#5e5e5e', cursor: 'pointer'}} onClick={prev => setShowLogin(false)}>Click here</span> to login.</Typography>
          </Grid>
        }
        </>
      }
      <Grid container>
        <Grid item xs={12}>
          <Cart />
        </Grid>
        { hasItems &&
          <Grid item xs={12} sx={{textAlign: 'right'}} p={3} >
            <TextField className={styles.couponInput} helperText={couponError} label="Coupon Code" value={couponCode.toUpperCase()} onChange={(e) => setCouponCode(e.target.value)} /><Button disableElevation sx={{padding: '1rem', borderRadius: '0px 6px 6px 0px'}} onClick={ checkCoupon } variant="contained">Check Code</Button>
        </Grid>        
        }
        { hasItems &&
          <Grid container style={{textAlign: 'right'}} justifyContent="flex-end">
            <Grid item xs={12} sm={8} p={3}>
              { (cartTotal() > 0.00) ? <PayPalComponent /> : <Button onClick={handleFreeOrder} color="amber" style={{ padding: '1rem' }} variant="contained">Claim Product(s)</Button>} 
            </Grid>
          </Grid>
        }
      </Grid>
    </Grid>
  )
}

functions/index.js 文件(firebase 函数文件):

const firebase = require('firebase-admin');
const functions = require('firebase-functions');

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//   functions.logger.info("Hello logs!", {structuredData: true});
//   response.send("Hello from Firebase!");
// });

exports.newsletterSignup = functions.https.onCall((data, ctx) => {
  console.log('ctx obj', ctx, 'ctx req', ctx.req)
  const sgMail = require('@sendgrid/mail')
  sgMail.setApiKey(process.env.SENDGRID_API_KEY)
  const msg = ("hello")
  res.send('Received').end()
})

exports.createFreeOrder = functions.https.onCall(async (data, ctx) => {
  const firebase = require('firebase-admin')
  const db = require('firebase/firebase-firestore')
  console.log('data and ctx', data, ctx)
})

exports.createPaypalOrder = functions.https.onCall(async (data, ctx) => {
  const checkoutNodeSDK = require('@paypal/checkout-server-sdk')
  const Prismic = require('@prismicio/client')
  const products = data.products

  const env = () => {
    const clientId = process.env.PAYPAL_CLIENT.id
    const clientSecret = process.env.PAYPAL_CLIENT.clientSecret

    return new checkoutNodeSDK.core.SandboxEnvironment(clientId, clientSecret)
  }

  const client = () => {
    return new checkoutNodeSDK.core.PayPalHttpClient(env)
  }

  // The request for PayPal
  const request = new paypal.orders.OrdersCreateRequest()
  request.prefer('return=representation')
  request.requestBody({
    intent: 'Capture',
    purchase_units: [{
      amount: {
        currency_code: 'USD',
        value: 0
      }
    }]
  })

  let order
  try {
    order = await client().execute(request)
  } catch {

  }
})

问题出在这里:

export const functions = dev ? app.functions().useEmulator("localhost", '5001') : app.functions()

httpsCallableuseEmulator 方法中不存在。相反,您应该在尝试像这样使用模拟器之前指定使用模拟器:

const dev = true;
const functions = app.functions();
// Checking if dev is true. If yes, use emulator
if (dev) functions.useEmulator("localhost", 5001)
export {functions}