useAuth() 钩子未正确设置用户,导致屏幕陷入受保护状态

问题描述 投票:0回答:0

我正在使用 expo 构建一个 React Native 应用程序,并尝试通过上下文向应用程序的其余部分添加身份验证。 应用程序确实正确登录,当表单提交时,我通过 AuthStack 中的 console.log 以及 Firebase 控制台获得确认;但是,为了处理应用程序其余部分的渲染,我似乎陷入了登录屏幕。

useAuth.js 在这里,我创建了一个 use auth hook 并登录 props 以传递到我的 AuthStackScreens 中,以便我可以启动登录。然后将其传递到上下文,以便应用程序可以使用它。

 // ... imports



 const AuthContext = createContext({})  
 
 
 export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [loadingInitial, setLoadingInitial] = useState(true)

  //implicitly returns an unsubscribe
  useEffect(
    () => 
    onAuthStateChanged(auth, (user) => {      
    if(user) {
        //logged in
        setUser(user);
        

      } else {

        setUser(null)
      }

      setLoading(false)
      setLoadingInitial(false)

    }),
    []
  );
    

  const signIn = async(userEmail, userPassword) => {
      try {
        const userCredential = await signInWithEmailAndPassword(auth, userEmail, userPassword );
        setUser(userCredential.user);       
       
      }
      catch (error) {
        console.error('error Signing in ', error.message);
      }
    }  
    const memoedValue = useMemo(() => ({
      user, 
      loading,       
      signIn
    }), [user, loading, error ] )
  
    console.log("auth" , user)
   return (
     <AuthContext.Provider
      value={memoedValue}
     >
     {!loadingInitial && children}
      </AuthContext.Provider>
   )
 }
 
 export default function useAuth() {

 
  return useContext(AuthContext);
  
 }
 
 const styles = StyleSheet.create({})
// ... (imports)

const AuthStack = createNativeStackNavigator();

//Schema to send data for auth

export const loginSchema = yup.object().shape({
  email: yup
  .string()
  .email("Invalid Email Format")
  .required("Email is a Required Field"),
  password: yup.string().required("Password is a Required field"),
})
// Handle Error 
function  ErrorText  ({ name, errors}) {
  if (!errors) {
    return null
  }

  return (
    <View style={{paddingLeft: 8, color: "red"}}>
      {errors[name] && (
        <Text style={{color:'#d81010'}}>{errors?.[name]?.message}</Text>
      )}


    </View>
  );
};
//Receive and deploy error
const ErrorAlert = ({title, message}) =>
  Alert.alert(title, message, [
    {text: "OK", onPress: () => console.log("ok Pressed")}
  ])
//Condense setting data in state 
export  function LoginScreen({navigation}) {
  const{ signIn, loading } = useAuth()


  //stores form inputs
  const {
    register, 
    setValue, 
    getValues, 
    handleSubmit, 
    // control, 
    // reset, 
    formState:  {errors},
  } = useForm({
    resolver: yupResolver(loginSchema),
    defaultValues: {
      email: '',
      password: "",
    },
  });

  useEffect (() => {
      register("email");
      register("password");
  }, [] )


// handles log in
 async function doLogin (data) {
      const { email, password} = data
      
      console.log(data)
      try {
        await signIn(email, password)
        console.log("signed In!!!")
          // commented out navigation as per react navigation auth flow discourages manual   navigation after on log in, should be handled by authcontext

        // navigation.navigate('HomeTabs')
       
      }
      catch (error) {
        Alert.alert('Error Logging In', error.message);
      }
  }
  

  
  // unused function 
  function onConfirmHandler() {
    // Confirm sign in 
    //navigate to home
    // navigation.navigate(HomeScreen)
    
  }
  function onPressSignUp () {
    navigation.navigate("SignUpScreen")
  }


  return (
    <View>
      <Text> Email</Text>
      <TextInput 
      id="email"
      textContentType='emailAddress'
      autoCapitalize='none'
      style={styles.input}
      onChangeText={(text)=> setValue("email", text)}      
      />
      
      <ErrorText name="email" errors={errors}> </ErrorText>
      <Text> Password</Text>
      <TextInput 
      id="password"
      textContentType='password'
      secureTextEntry={true}
      style={styles.input}
      autoCapitalize='none'
      onChangeText={(text)=> setValue("password", text)}
     
      
      />
      
      
      <ErrorText name="password" errors={errors}> </ErrorText>
      
      <View style={styles.buttonContainer}> 
      <Button  style={styles.button}onPress={handleSubmit(doLogin)} > Log In </Button>
     
      <Text  style={styles.buttonText}> Don't have an account? </Text>

      <Button style={styles.button} onPress={onPressSignUp} > Create Account </Button>

      </View>
    </View>
  )
}
//sign up currently work in progress after log in becomes successful 
export  function SignUpScreen({navigation}) {
 ...


function AuthStackScreens() {
  return <AuthStack.Navigator > 
    <AuthStack.Screen name="LoginScreen" component={LoginScreen}/>
    <AuthStack.Screen name="SignUpScreen" component={SignUpScreen}/>
    {/* navigate to profilecreate screen? */}
 
  </AuthStack.Navigator>
}

export default AuthStackScreens

const styles = StyleSheet.create({
 // ... styling

})

这里登录当前正常工作,表单提交,useAuth.js 中的 console.log 触发,我确实看到我获取了所有用户数据。


// ...imports


const MainStack = createNativeStackNavigator();
const BottomTab = createBottomTabNavigator();
const ProfileStack = createNativeStackNavigator();

function HomeTabs() {
  return (
    <BottomTab.Navigator screenOptions={{ headerShown: false }}>
      <BottomTab.Screen
        name="HomeScreen"
        component={HomeScreen}
        options={{
          tabBarIcon: ({ color, size }) => <Ionicons name="home" color={color} size={size} />,
          headerShown: false,
        }}
      />
      <BottomTab.Screen
        name="Profile"
        component={ProfileStackScreens}
        options={{
          tabBarIcon: ({ color, size }) => <Ionicons name="person" color={color} size={size} />,
          headerShown: false,
        }}
      />
    </BottomTab.Navigator>
  );
}

function ProfileStackScreens() {
  return (
    <ProfileStack.Navigator>
      <ProfileStack.Screen name="ProfileScreen" component={ProfileScreen} />
    </ProfileStack.Navigator>
  );
}

export default function App() {
  const { user, loading } = useAuth();
// console.log returns undefined for user, fires first when app builds
  console.log(user, loading)
  if (loading) {
    return <View><Text>loading. .. .</Text></View>;
  }

  return (
    <SafeAreaView style={styles.screen}>
      <AuthProvider>
      <NavigationContainer>
          <UsersContextProvider>
            <MainStack.Navigator>
              {user ? (
                <>
                  <MainStack.Screen name="HomeTabs" component={HomeTabs} options={{ headerShown: false }} />
                  <MainStack.Screen name="CardDetails" component={CardDetails} />
                  <MainStack.Screen name="Profile" component={ProfileStackScreens} />
                  <MainStack.Screen name="ProfileCreateScreen" component={ProfileCreateScreen} />
                </>
              ) : (
                <MainStack.Screen name="Auth" component={AuthStackScreens} options={{ headerShown: false }} />
              )}
            </MainStack.Navigator>
          </UsersContextProvider>
      </NavigationContainer>
        </AuthProvider>
    </SafeAreaView>
  );
}

现在这就是我认为问题所在的地方,我正在使用嵌套导航器,我认为问题在于我如何尝试使 AuthProvider 成为一个更高的订单组件。我认为我做得正确。我怀疑问题在于,当其他组件登录时,app.js 中的 useAuth 挂钩不会更新。我尝试在 useAuth.js 中使用 useReducer 进行重构,但这也不起作用。经过几个小时的研究,我想知道是否还有其他人可以提供新的视角。谢谢你。

编辑:

因此,将导航组件分成单独的文件后,useAuth 检查发生在 MainStackScreen 组件文件中,而不是 app.js 中,这样应用程序可以首先触发并正确查找其他组件。进行这些更改后,登录即可正确进入主屏幕。

firebase react-native firebase-authentication react-context react-native-navigation
© www.soinside.com 2019 - 2024. All rights reserved.