简体   繁体   中英

Expo react-native app with firebase phone authentication works on web, error on ios simulator and crashes with no warning on Android

I am building a react-native app with expo, I have only 2 components, WelcomeScreen and PhoneLoginScreen. I am trying to implement firebase phone authentication which works fine on the Web but on iOS Simulator I get an error Verifier._reset is not a function. (In 'verifier._reset()', 'verifiier._reset' is undefined Verifier._reset is not a function. (In 'verifier._reset()', 'verifiier._reset' is undefined and on Android, it just crashes when I click the continue button that navigates to the PhoneLoginScreen component. Codes below:

App.js

import React from "react"

import { NavigationContainer } from "@react-navigation/native"
import { createNativeStackNavigator } from "@react-navigation/native-stack"

import WelcomeScreen from "./components/WelcomeScreen"
import PhoneLoginScreen from "./components/auth/PhoneLoginScreen"

const Stack = createNativeStackNavigator()

export default function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName="Welcome">
                <Stack.Screen
                    name="Welcome"
                    component={WelcomeScreen}
                    options={{ headerShown: false }}
                />

                <Stack.Screen
                    name="PhoneLogin"
                    component={PhoneLoginScreen}
                    options={{ headerShown: false }}
                />
            </Stack.Navigator>
        </NavigationContainer>
    )
}

WelcomeScreen.js

import React from "react"
import { Text, View, Button } from "react-native"

export default function WelcomeScreen({ navigation }) {
    return (
        <View
            style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
            <Text>Welcome</Text>
            <Button
                title="Continue"
                onPress={() => navigation.navigate("PhoneLogin")}
            />
        </View>
    )
}

PhoneLoginScreen.js

import React, { useRef, useState } from "react"
import { firebaseApp, auth } from "../../firebase"
import {
    Text,
    View,
    TextInput,
    Button,
    StyleSheet,
    TouchableOpacity,
} from "react-native"

import {
    FirebaseRecaptchaVerifierModal,
    FirebaseRecaptchaBanner,
} from "expo-firebase-recaptcha"

import { PhoneAuthProvider, signInWithCredential } from "firebase/auth"

export default function PhoneLoginScreen() {
    const recaptchaVerifier = useRef(null)
    const [message, showMessage] = useState()
    const [phoneNumber, setPhoneNumber] = useState()
    const [verificationId, setVerificationId] = useState()
    const [verificationCode, setVerificationCode] = useState()

    const firebaseConfig = firebaseApp ? firebaseApp.options : undefined
    const attemptInvisibleVerification = true

    return (
        <View style={styles.center}>
            <FirebaseRecaptchaVerifierModal
                ref={recaptchaVerifier}
                firebaseConfig={firebaseConfig}
                attemptInvisibleVerification={attemptInvisibleVerification}
            />

            <Text style={{ marginTop: 20 }}>Enter phone number</Text>

            <TextInput
                style={{ marginVertical: 10, fontSize: 17 }}
                placeholder="+1 999 999 9999"
                autoFocus
                autoCompleteType="tel"
                keyboardType="phone-pad"
                textContentType="telephoneNumber"
                onChangeText={phoneNumber => setPhoneNumber(phoneNumber)}
            />

            <Button
                title="Send Verification Code"
                disabled={!phoneNumber}
                onPress={async () => {
                    try {
                        const phoneProvider = new PhoneAuthProvider(auth)
                        const verificationId =
                            await phoneProvider.verifyPhoneNumber(
                                phoneNumber,
                                recaptchaVerifier.current
                            )
                        setVerificationId(verificationId)
                        showMessage({
                            text: "Verification code has been sent to your phone.",
                        })
                    } catch (err) {
                        showMessage({
                            text: `Error 111: ${err.message}`,
                            color: "red",
                        })
                    }
                }}
            />
            <Text style={{ marginTop: 20 }}>Enter Verification code</Text>
            <TextInput
                style={{ marginVertical: 10, fontSize: 17 }}
                editable={!!verificationId}
                placeholder="123456"
                onChangeText={setVerificationCode}
            />
            <Button
                title="Confirm Verification Code"
                disabled={!verificationId}
                onPress={async () => {
                    try {
                        const credential = PhoneAuthProvider.credential(
                            verificationId,
                            verificationCode
                        )

                        await signInWithCredential(auth, credential)
                        showMessage({
                            text: "Phone authentication successful 👍",
                        })
                    } catch (err) {
                        showMessage({
                            text: `Error: ${err.message}`,
                            color: "red",
                        })
                    }
                }}
            />
            {message ? (
                <TouchableOpacity
                    style={[
                        StyleSheet.absoluteFill,
                        {
                            backgroundColor: 0xffffffee,
                            justifyContent: "center",
                        },
                    ]}
                    onPress={() => showMessage(undefined)}>
                    <Text
                        style={{
                            color: message.color || "blue",
                            fontSize: 17,
                            textAlign: "center",
                            margin: 20,
                        }}>
                        {message.text}
                    </Text>
                </TouchableOpacity>
            ) : undefined}
            {attemptInvisibleVerification && <FirebaseRecaptchaBanner />}
        </View>
    )
}

const styles = StyleSheet.create({
    center: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
    },
})

firebase.js

import firebase from "firebase/compat/app"
import "firebase/compat/auth"
import "firebase/compat/firestore"

// Initialize Firebase
const firebaseConfig = {
    // Config info...
}

let firebaseApp

if (firebase.apps.length === 0) {
    firebaseApp = firebase.initializeApp(firebaseConfig)
} else {
    firebaseApp = firebase.app()
}

const auth = firebase.auth()

export { auth, firebaseApp }

package.json dependencies

"dependencies": {
    "@react-navigation/native": "^6.0.6",
    "@react-navigation/native-stack": "^6.2.5",
    "expo": "~43.0.2",
    "expo-firebase-recaptcha": "~2.0.2",
    "expo-status-bar": "~1.1.0",
    "firebase": "^9.5.0",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-native": "0.64.3",
    "react-native-safe-area-context": "3.3.2",
    "react-native-screens": "~3.8.0",
    "react-native-web": "0.17.1",
    "react-native-webview": "11.13.0"
  }

I have googled forever and nothing works. Please advice

This is a bug. The maintainers of "expo-firebase-recaptcha" have yet to publish a fix, so until that day comes, this is how you fix it yourself:

Go to node_modules/expo-firebase-recaptcha, open the build folder and find FirebaseRecaptchaVerifierModal.js .

Inside of FirebaseRecaptchaVerifierModal, add the following function to the component definition:

_reset = () => {}

I've included a snippet of the file after adding the empty function definition:

FirebaseRecaptchaVerifierModal.js

[...]
            else {
                this.setState({
                    visible: true,
                    visibleLoaded: false,
                    resolve,
                    reject,
                });
            }
        });
    }
    
    /**
     * Add the following line anywhere inside of the FirebaseRecaptchaVerifierModal component.
     */
    _reset = () => {}

    onVisibleLoad = () => {
        this.setState({
            visibleLoaded: true,
        });
    };
[...]

Note: You will have to do this after every yarn/npm installl or change in node_modules until the publishers push an update.

Error: verifier._reset is not a function. when trying to Sign in with phone using firebase, react native and Expo

FirebaseRecaptchaVerifierModal attemptInvisibleVerification Crashes on Android Emulator look at this.This is help me so well

I add

`<FirebaseRecaptchaVerifierModal

ref={recaptchaVerifierRef}

firebaseConfig={firebaseConfig}

androidHardwareAccelerationDisabled

attemptInvisibleVerification />` this line on my FirebaseRecaptchaVerifierModal this gonna help to my to problem.

** FIX FOR EXPO 45, 46 **

After Surfing through the internet for answers, i failed and din't find anything to make it work on Expo 45 and Expo 46, finally took it to my hands because We can't ship an app with manual recaptcha as it is very bad UX. So after trying every possible prop in the FirebaseRecaptchaVerifierModal, I FINALLY FOUND THE WORKING FIX

By Combining all the fixes and adding one of my own found, I finally can rest in peace as this is now working

// Declare the Timeout for Initial Page Load Fix
  const [isInit, setisInit] = useState(false)
  useEffect(() => {
    setTimeout(function () {
      setisInit(true)
    }, 1000)
    return () => {
      setisInit(false)
    }
  }, [])

return(
<View>
...
        {isInit && (
          <FirebaseRecaptchaVerifierModal
            ref={recaptchaVerifier}
            firebaseConfig={app.options}
            androidHardwareAccelerationDisabled={true}
            androidLayerType="software"
            attemptInvisibleVerification
          />
        )}
...

      <FirebaseRecaptchaBanner />
</View>

This was the magical line

 androidLayerType="software"

I Hope Expo Updates there Documentation and avoid this painful experience for all of us Developers, I mean I literally never have seen an app with manual Recaptcha until im spamming

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM