反应天然。火基。如何等待异步函数执行?

发布于 2025-02-13 12:39:53 字数 7024 浏览 1 评论 0原文

当进入电话登录屏幕(下面的代码)时,我想立即检查(在使用效果中),用户已被授权并拥有一个帐户(在Getuser中)。验证后,导航到相应的屏幕。问题是导航需要几秒钟(因为Getuser是一个异步函数)。如何使使用效果的代码等待函数getuser完成,获取结果然后导航?

import React, { useEffect, useState, useContext, useRef } from "react";
import {
  Text,
  StyleSheet,
  View,
  TextInput,
  TouchableOpacity,
} from "react-native";

import CustomButton from "../components/CustomButton";

import DataContext from "../context/DataContext";

import { app, auth, db } from "../../firebase";
import { doc, getDoc } from "firebase/firestore";
import {
  FirebaseRecaptchaVerifierModal,
  FirebaseRecaptchaBanner,
} from "expo-firebase-recaptcha";
import {
  PhoneAuthProvider,
  signInWithCredential,
  onAuthStateChanged,
} from "firebase/auth";

const SignupPhoneScreen = ({ navigation }) => {
  const { phone, setPhone } = useContext(DataContext);
  const [phoneError, setPhoneError] = useState("");

  const recaptchaVerifier = useRef(null);
  const [verificationId, setVerificationId] = useState();
  const [verificationCode, setVerificationCode] = useState();

  const firebaseConfig = app ? app.options : undefined;
  const [message, showMessage] = useState();
  const attemptInvisibleVerification = false;

  const [userInfo, setUserInfo] = useState({
    status: "loading",
    data: null,
    error: null,
  });

  const [isSigned, setIsSigned] = useState(false);

  useEffect(() => {
    console.log("SignupPhoneScreen. useEffect");

    const getUser = async (phoneNumber) => {
      console.log("SignupPhoneScreen. useEffect. getUser");
      const docRef = doc(db, "users", phoneNumber);
      const docSnap = await getDoc(docRef);

      if (docSnap.exists()) {
        console.log(
          "SignupPhoneScreen. useEffect. getUser. last_name=",
          docSnap.data().last_name
        );
        console.log(
          "SignupPhoneScreen. useEffect. getUser. data=",
          docSnap.data()
        );
        // setUserInfo({ status: "fetched", data: docSnap.data(), error: null });
        navigation.navigate("MainHome");
      } else {
        // setUserInfo({ status: "fetched", data: null, error: null });
        setPhone(phoneNumber);
        navigation.navigate("SignupLanguage");
      }
    };

    const unsubscribe = onAuthStateChanged(auth, (user) => {
      if (user) {
        console.log(
          "SignupPhoneScreen. useEffect. user.phoneNumber=",
          user.phoneNumber
        );

        console.log("SignupPhoneScreen. useEffect. Before getUser", userInfo);

        getUser(user.phoneNumber);

        console.log("SignupPhoneScreen. useEffect. After getUser", userInfo);
      }
    });

    return unsubscribe;
  }, [isSigned]);

  const sendVerification = async () => {
    console.log("SignupPhoneScreen. sendVerification");

    try {
      const phoneProvider = new PhoneAuthProvider(auth);
      const verificationId = await phoneProvider.verifyPhoneNumber(
        phone,
        recaptchaVerifier.current
      );
      setVerificationId(verificationId);
      showMessage({
        text: "Verification code has been sent to your phone",
      });
    } catch (err) {
      showMessage({ text: `Error: ${err.message}`, color: "red" });
    }
  };

  const confirmCode = async () => {
    console.log("SignupPhoneScreen. confirmCode");

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

  const handleSubmit = () => {
    console.log("SignupPhoneScreen. handleSubmit");

    var phoneLength = phone.trim().length;

    if (phoneLength == 0) {
      setPhoneError("Phone is required");
    } else if (phoneLength < 9) {
      setPhoneError("Phone should be minimum 9 characters");
    } else {
      setPhoneError("");
      sendVerification();
    }
  };

  const isPhoneValid = () => {
    return phoneError.length == 0;
  };

  const handleNavigation = () => {
    console.log("SignupPhoneScreen. handleNavigation");

    var user = auth.currentUser;

    console.log("SignupPhoneScreen. handleNavigation. user=", user);

    showMessage(undefined);

    if (user) {
      setIsSigned(true);
    }
  };

  return (
    <View style={styles.containerGeneral}>
      <View style={styles.containerRecaptcha}>
        <FirebaseRecaptchaVerifierModal
          ref={recaptchaVerifier}
          firebaseConfig={firebaseConfig}
          // attemptInvisibleVerification
        />
      </View>
      <View style={styles.containerCaption}>
        <Text style={styles.textCaption}>Enter your phone number</Text>
      </View>
      <View style={styles.containerInput}>
        <TextInput
          style={styles.input}
          placeholder="+79999999999"
          // autoFocus
          keyboardType="phone-pad"
          defaultValue={phone}
          onChangeText={(value) => setPhone(value)}
        />
      </View>
      <View style={styles.containerError}>
        <Text
          style={[
            styles.textSmall,
            { color: isPhoneValid() ? "transparent" : "red" },
          ]}
        >
          {phoneError}
        </Text>
      </View>
      <View style={styles.containerText}>
        <Text style={styles.textSmall}>
          We will send an SMS to this number with a confirmation code.
        </Text>
      </View>
      <View style={styles.containerButton}>
        <CustomButton title="Send Verification Code" onPress={handleSubmit} />
      </View>
      <View style={styles.containerInputLabel}>
        <Text style={styles.textLabel}>Enter verification code</Text>
      </View>
      <View style={[styles.containerInput, { marginTop: 0 }]}>
        <TextInput
          style={styles.input}
          editable={!!verificationId}
          placeholder="123456"
          keyboardType="number-pad"
          onChangeText={setVerificationCode}
        />
      </View>
      <View style={styles.containerButton}>
        <CustomButton title="Confirm Verification Code" onPress={confirmCode} />
      </View>
      {message ? (
        <TouchableOpacity
          style={[
            StyleSheet.absoluteFill,
            { backgroundColor: 0xffffffee, justifyContent: "center" },
          ]}
          // onPress={() => showMessage(undefined)}
          onPress={handleNavigation}
        >
          <Text style={[styles.textPopup, { color: message.color }]}>
            {message.text}
          </Text>
        </TouchableOpacity>
      ) : undefined}
      {attemptInvisibleVerification && <FirebaseRecaptchaBanner />}
    </View>
  );
};

export default SignupPhoneScreen;

When going to the phone login screen (its code below), I want to immediately check (in useEffect) that a user is authorized and has an account (in getUser). After that validation, navigate to the corresponding screen. The problem is that navigation takes a few seconds (because of getUser is an async function). How can I make the code in useEffect wait for function getUser to complete, get the result, and then navigate?

import React, { useEffect, useState, useContext, useRef } from "react";
import {
  Text,
  StyleSheet,
  View,
  TextInput,
  TouchableOpacity,
} from "react-native";

import CustomButton from "../components/CustomButton";

import DataContext from "../context/DataContext";

import { app, auth, db } from "../../firebase";
import { doc, getDoc } from "firebase/firestore";
import {
  FirebaseRecaptchaVerifierModal,
  FirebaseRecaptchaBanner,
} from "expo-firebase-recaptcha";
import {
  PhoneAuthProvider,
  signInWithCredential,
  onAuthStateChanged,
} from "firebase/auth";

const SignupPhoneScreen = ({ navigation }) => {
  const { phone, setPhone } = useContext(DataContext);
  const [phoneError, setPhoneError] = useState("");

  const recaptchaVerifier = useRef(null);
  const [verificationId, setVerificationId] = useState();
  const [verificationCode, setVerificationCode] = useState();

  const firebaseConfig = app ? app.options : undefined;
  const [message, showMessage] = useState();
  const attemptInvisibleVerification = false;

  const [userInfo, setUserInfo] = useState({
    status: "loading",
    data: null,
    error: null,
  });

  const [isSigned, setIsSigned] = useState(false);

  useEffect(() => {
    console.log("SignupPhoneScreen. useEffect");

    const getUser = async (phoneNumber) => {
      console.log("SignupPhoneScreen. useEffect. getUser");
      const docRef = doc(db, "users", phoneNumber);
      const docSnap = await getDoc(docRef);

      if (docSnap.exists()) {
        console.log(
          "SignupPhoneScreen. useEffect. getUser. last_name=",
          docSnap.data().last_name
        );
        console.log(
          "SignupPhoneScreen. useEffect. getUser. data=",
          docSnap.data()
        );
        // setUserInfo({ status: "fetched", data: docSnap.data(), error: null });
        navigation.navigate("MainHome");
      } else {
        // setUserInfo({ status: "fetched", data: null, error: null });
        setPhone(phoneNumber);
        navigation.navigate("SignupLanguage");
      }
    };

    const unsubscribe = onAuthStateChanged(auth, (user) => {
      if (user) {
        console.log(
          "SignupPhoneScreen. useEffect. user.phoneNumber=",
          user.phoneNumber
        );

        console.log("SignupPhoneScreen. useEffect. Before getUser", userInfo);

        getUser(user.phoneNumber);

        console.log("SignupPhoneScreen. useEffect. After getUser", userInfo);
      }
    });

    return unsubscribe;
  }, [isSigned]);

  const sendVerification = async () => {
    console.log("SignupPhoneScreen. sendVerification");

    try {
      const phoneProvider = new PhoneAuthProvider(auth);
      const verificationId = await phoneProvider.verifyPhoneNumber(
        phone,
        recaptchaVerifier.current
      );
      setVerificationId(verificationId);
      showMessage({
        text: "Verification code has been sent to your phone",
      });
    } catch (err) {
      showMessage({ text: `Error: ${err.message}`, color: "red" });
    }
  };

  const confirmCode = async () => {
    console.log("SignupPhoneScreen. confirmCode");

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

  const handleSubmit = () => {
    console.log("SignupPhoneScreen. handleSubmit");

    var phoneLength = phone.trim().length;

    if (phoneLength == 0) {
      setPhoneError("Phone is required");
    } else if (phoneLength < 9) {
      setPhoneError("Phone should be minimum 9 characters");
    } else {
      setPhoneError("");
      sendVerification();
    }
  };

  const isPhoneValid = () => {
    return phoneError.length == 0;
  };

  const handleNavigation = () => {
    console.log("SignupPhoneScreen. handleNavigation");

    var user = auth.currentUser;

    console.log("SignupPhoneScreen. handleNavigation. user=", user);

    showMessage(undefined);

    if (user) {
      setIsSigned(true);
    }
  };

  return (
    <View style={styles.containerGeneral}>
      <View style={styles.containerRecaptcha}>
        <FirebaseRecaptchaVerifierModal
          ref={recaptchaVerifier}
          firebaseConfig={firebaseConfig}
          // attemptInvisibleVerification
        />
      </View>
      <View style={styles.containerCaption}>
        <Text style={styles.textCaption}>Enter your phone number</Text>
      </View>
      <View style={styles.containerInput}>
        <TextInput
          style={styles.input}
          placeholder="+79999999999"
          // autoFocus
          keyboardType="phone-pad"
          defaultValue={phone}
          onChangeText={(value) => setPhone(value)}
        />
      </View>
      <View style={styles.containerError}>
        <Text
          style={[
            styles.textSmall,
            { color: isPhoneValid() ? "transparent" : "red" },
          ]}
        >
          {phoneError}
        </Text>
      </View>
      <View style={styles.containerText}>
        <Text style={styles.textSmall}>
          We will send an SMS to this number with a confirmation code.
        </Text>
      </View>
      <View style={styles.containerButton}>
        <CustomButton title="Send Verification Code" onPress={handleSubmit} />
      </View>
      <View style={styles.containerInputLabel}>
        <Text style={styles.textLabel}>Enter verification code</Text>
      </View>
      <View style={[styles.containerInput, { marginTop: 0 }]}>
        <TextInput
          style={styles.input}
          editable={!!verificationId}
          placeholder="123456"
          keyboardType="number-pad"
          onChangeText={setVerificationCode}
        />
      </View>
      <View style={styles.containerButton}>
        <CustomButton title="Confirm Verification Code" onPress={confirmCode} />
      </View>
      {message ? (
        <TouchableOpacity
          style={[
            StyleSheet.absoluteFill,
            { backgroundColor: 0xffffffee, justifyContent: "center" },
          ]}
          // onPress={() => showMessage(undefined)}
          onPress={handleNavigation}
        >
          <Text style={[styles.textPopup, { color: message.color }]}>
            {message.text}
          </Text>
        </TouchableOpacity>
      ) : undefined}
      {attemptInvisibleVerification && <FirebaseRecaptchaBanner />}
    </View>
  );
};

export default SignupPhoneScreen;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

雪化雨蝶 2025-02-20 12:39:53

您可以尝试添加一个加载程序,

const [isLoading, setIsLoading] = useState(false);


// The render
if (isLoading) {
    return (
      <Text>I'm loading</Text>
    );
  }

如果用户获得授权并具有帐户,则可以添加使用效果,

  useEffect(() => {
   if(user have an account) {
      navigate('Screen');
    } else {
    // the the phone login screen can display
    setIsLoading(false)
    
    } 
   }, []);

最好的方法是在app.js中使用多个路由器

但 https://reaectnavigation.org/docs/auth-flow/“ rel =” nofollow noreferrer“> https://reactnavigation.org/docs/auth-flow/

You can try to add a loader, u can add

const [isLoading, setIsLoading] = useState(false);


// The render
if (isLoading) {
    return (
      <Text>I'm loading</Text>
    );
  }

And then in the useEffect if the user is authorized and has an account

  useEffect(() => {
   if(user have an account) {
      navigate('Screen');
    } else {
    // the the phone login screen can display
    setIsLoading(false)
    
    } 
   }, []);

But the best way is to have multiple router in your App.js

You can find an example here

https://reactnavigation.org/docs/auth-flow/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文