typescriptreact-nativereact-typescript

React Ignite, SignupScreen, post submission is not transitioning to welcome screen/authenticating


I'm using the React Ignite boilerplate and have firebase as the backend/auth. when I hit submit in my signup screen, the account is created in the firebase console, and I can hit the login button and then sign in successfully. However I would expect that when I hit signup (and it's successful) I transition to the welcome screen and am authenticated.

Here is the signup screen in full.

    import { observer } from "mobx-react-lite"
    import React, { ComponentType, FC, useEffect, useMemo, useRef, useState } from "react"
    import { Alert, TextInput, TextStyle, TouchableOpacity, ViewStyle } from "react-native"
    import { Button, Icon, Screen, Text, TextField, TextFieldAccessoryProps } from "../components"
    import { useStores } from "../models"
    import { AppStackScreenProps } from "../navigators"
    import { colors, spacing } from "../theme"
    import { auth, database } from "../../config/firebase";
    import { createUserWithEmailAndPassword } from "firebase/auth";
    import { authenticationStore } from "../authenticationStore";
    import { doc, setDoc } from "firebase/firestore"
    import { useNavigation } from "@react-navigation/native"
    
    interface SignUpScreenProps extends AppStackScreenProps<"SignUp"> { }
    
    export const SignUpScreen: FC<SignUpScreenProps> = observer(function SignUpScreen() {
    
      const navigation = useNavigation()
    
      const goToLogin = () => {
        navigation.navigate("Login");
      }
    
      const onHandleSignup = async () => {
        try {
          const userCredential = await createUserWithEmailAndPassword(auth, email, password);
          const user = userCredential.user;
          const userRef = doc(database, "users", user.uid);
          await setDoc(userRef, {
            displayName: name,
            email: email,
            uid: user.uid,
            photoURL: "",
            phoneNumber: "",
          })
        } catch (error) {
          if (error instanceof Error) {
            Alert.alert(error.message);
          } else {
            // Handle any other types of errors or objects
            Alert.alert('An unknown error occurred');
          }
        }
      };
      const [name, setName] = useState("");
      const [email, setEmail] = useState("");
      const [password, setPassword] = useState("");
      const [imageURL, setImageURL] = useState("");
    
    
      const [authPassword, setAuthPassword] = useState("")
      const [isAuthPasswordHidden, setIsAuthPasswordHidden] = useState(true)
      const [isSubmitted, setIsSubmitted] = useState(false)
      const [attemptsCount, setAttemptsCount] = useState(0)
      const {
        authenticationStore: { authEmail, setAuthEmail, setAuthToken, validationError },
      } = useStores()
    
      useEffect(() => {
        // Here is where you could fetch credentials from keychain or storage
        // and pre-fill the form fields.
        setAuthEmail("ignite@infinite.red")
        setAuthPassword("ign1teIsAwes0m3")
    
        // Return a "cleanup" function that React will run when the component unmounts
        return () => {
          setAuthPassword("")
          setAuthEmail("")
        }
      }, [])
    
    
    
    
      return (
        <Screen
          preset="auto"
          contentContainerStyle={$screenContentContainer}
          safeAreaEdges={["top", "bottom"]}
        >
          <Text testID="login-heading" tx="signUpScreen.signIn" preset="heading" style={$signIn} />
          <Text tx="signUpScreen.enterDetails" preset="subheading" style={$enterDetails} />
          {attemptsCount > 2 && <Text tx="signUpScreen.hint" size="sm" weight="light" style={$hint} />}
    
    
    
          <TextField
            value={email}
            onChangeText={setEmail}
            containerStyle={$textField}
            autoCapitalize="none"
            autoComplete="email"
            autoCorrect={false}
            keyboardType="email-address"
            labelTx="signUpScreen.emailFieldLabel"
            placeholderTx="signUpScreen.emailFieldPlaceholder"
          />
    
          <TextField
            value={name}
            onChangeText={setName}
            containerStyle={$textField}
            autoCapitalize="none"
            autoCorrect={false}
            labelTx="signUpScreen.nameFieldLabel"
            placeholderTx="signUpScreen.nameFieldPlaceholder"
    
          />
    
          <TextField
            value={password}
            onChangeText={setPassword}
            containerStyle={$textField}
            autoCapitalize="none"
            autoComplete="password"
            autoCorrect={false}
            secureTextEntry={isAuthPasswordHidden}
            labelTx="signUpScreen.passwordFieldLabel"
            placeholderTx="signUpScreen.passwordFieldPlaceholder"
    
          />
    
    
    
          <Button
            testID="login-button"
            tx="signUpScreen.tapToSignIn"
            style={$tapButton}
            preset="reversed"
            onPress={onHandleSignup}
          />
    
          <TouchableOpacity style={$loginButton} onPress={goToLogin}>
            <Text>Have an accoount ? Login</Text>
          </TouchableOpacity>
    
    
        </Screen>
      )
    })
    
    const $screenContentContainer: ViewStyle = {
      paddingVertical: spacing.xxl,
      paddingHorizontal: spacing.lg,
    }
    
    const $signIn: TextStyle = {
      marginBottom: spacing.sm,
    }
    
    const $enterDetails: TextStyle = {
      marginBottom: spacing.lg,
    }
    
    const $hint: TextStyle = {
      color: colors.tint,
      marginBottom: spacing.md,
    }
    
    const $textField: ViewStyle = {
      marginBottom: spacing.lg,
    }
    
    const $tapButton: ViewStyle = {
      marginTop: spacing.xs,
    }
    
    const $loginButton: ViewStyle = {
      marginTop: spacing.xs,
      alignItems: 'center',
      padding: 10,
    }
    
    // @demo remove-file

When I look at the app navigator I can see that welcome screen is only available for logged in users

    {isAuthenticated ? (
            <>
              {/* @demo remove-block-end */}
              <Stack.Screen name="Welcome" component={Screens.WelcomeScreen} />
              {/* @demo remove-block-start */}
              <Stack.Screen name="Demo" component={DemoNavigator} />
            </>
          ) : (
            <>
              <Stack.Screen name="Login" component={Screens.LoginScreen} />
              <Stack.Screen name="SignUp"component={Screens.SignUpScreen} />
            </>
        )}

so I assumed that after sign up I need to authenticate the new user and then navigate to the welcome screen so I added the below after the phone number line

    const token = await user.getIdToken();
    authenticationStore.setAuthToken(token); 

    // Navigate to the Welcome screen
    navigation.navigate("Welcome");

But soon as I add anything after phone number I get uncaught exception on the line, I tried adding console.log and nothing is coming in the console either. it seems I can't edit after this without breaking the screen...

    const $screenContentContainer: ViewStyle = {
      paddingVertical: spacing.xxl,
      paddingHorizontal: spacing.lg,
    }

Thanks for your help


Solution

  • From looking at the code, I highly recommend removing the $ sign especially at the start of variables. It can cause parsing issues. And that is likely what is happening.

    1. The app launches / loads on initial bundle download or full refresh.
    2. The app fails when you save it.

    These symptoms sound like a "live reload" issue where the updated code is corrupted.

    Other section that looks weird is here. This section is calling setState when unmounting. Which is a NO-OP/does nothing in theory. But during live-reload it could possibly cause the previous component instance (being torn down) to be kept in memory longer.

    useEffect(() => {
       ....
       // Return a "cleanup" function that React will run when the component unmounts
        return () => {
          setAuthPassword("")
          setAuthEmail("")
        }
      }, [])
    

    Recommendations

    1. Remove all the $ variable references that are prefixed to your code. It may not be the problem. But for best practices, I highly recommend avoiding that sort of naming structure.
    2. Turn off live reloading. See if it works. If so likely that's the problem. Turn on live reloading again, just flip it to confirm that saving is not causing some other side effect.
    3. Check nodejs memory confirm you have enough. Confirm you have enough old space configured. Usually good number is above 4GB for the metro server only.
    4. Look at what your Screen components (above this example) are doing. Are they firing methods when the app refreshes? Same with mobx. Is the observer triggering on live-reload.
    5. Memoize the whole component. See if the parent is trigger the re-renders / infinite freeze.
    6. Add logs and step debug to see if the react thread is actually frozen or if something else is going on, such as a transparent view blocking touches.