使用Expo Go启动React Native应用时出现导航对象缺失渲染错误
修复“找不到导航对象”错误并实现Microsoft账户双因素登录
我正在实现基于Microsoft Account搭配Microsoft Authenticator的双因素登录功能,但测试时遇到以下错误:
Render Error. Couldn't find a navigation object. Is your component inside NavigationContainer?
我试过两段代码但都无效,代码如下:
尝试的第一段代码(类组件)
import React, { Component } from 'react'; import { View, Button, Text } from 'react-native'; import { UserAgentApplication } from 'react-native-msal'; import { AuthenticationParameters } from 'react-native-msal'; export default class Login extends Component { constructor(props) { super(props); this.auth = new UserAgentApplication({ auth: { clientId: 'My_Client_ID', authority: 'My_Authority', redirectUri: 'exp://85-nny.anonymous.donewithit.exp.direct:80/--/auth', // 不确定这个重定向URL是否有效 }, }); this.state = { accessToken: null, userInfo: null, }; } login = async () => { try { const authResult = await this.auth.login(); const accessToken = authResult.accessToken; if (accessToken) { this.setState({ accessToken }); this.getUserInfo(); } } catch (error) { console.error(error); } } getUserInfo = async () => { try { const authParameters = new AuthenticationParameters(); authParameters.scopes = ['openid', 'profile']; const userInfo = await this.auth.acquireTokenSilent(authParameters); this.setState({ userInfo }); } catch (error) { console.error(error); } } logout = () => { this.auth.logout(); this.setState({ accessToken: null, userInfo: null }); } render() { const { accessToken, userInfo } = this.state; return ( <View> {accessToken ? ( <Button title="Logout" onPress={this.logout} /> ) : ( <Button title="Login with Microsoft" onPress={this.login} /> )} {userInfo && ( <View> <Text>Welcome, {userInfo.displayName}</Text> <Text>Email: {userInfo.userPrincipalName}</Text> </View> )} </View> ); } } export default Login;
尝试的第二段代码(函数式组件)
import React from 'react'; import { View, Text, Button } from 'react-native'; import { authorize } from 'react-native-app-auth'; import { useNavigation } from '@react-navigation/native'; const Login = () => { const navigation = useNavigation(); const config = { clientId: 'My_Client_ID', redirectUrl: 'exp://85-nny.anonymous.donewithit.exp.direct:80/--/auth', scopes: ['openid', 'profile', 'email', 'offline_access', 'User.Read'], serviceConfiguration: { authorizationEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', tokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token', }, }; const loginWithMicrosoft = async () => { try { const result = await authorize(config); // 用户登录成功后跳转到首页 if (result.idToken) { navigation.navigate('HomeScreen'); } } catch (error) { console.error('Authentication Error:', error); } }; return ( <View> <Text>Login Page</Text> <Button title="Login with Microsoft" onPress={loginWithMicrosoft} /> </View> ); }; export default Login;
错误原因与修复方案
这个错误的核心是Login组件没有被嵌套在React Navigation的NavigationContainer内部,或者类组件没有正确获取导航实例。
1. 先配置根组件的NavigationContainer
确保App入口文件用NavigationContainer包裹整个导航结构,这是React Navigation的强制要求:
import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import Login from './Login'; import HomeScreen from './HomeScreen'; const Stack = createStackNavigator(); export default function App() { return ( <NavigationContainer> <Stack.Navigator initialRouteName="Login"> <Stack.Screen name="Login" component={Login} /> <Stack.Screen name="HomeScreen" component={HomeScreen} /> </Stack.Navigator> </NavigationContainer> ); }
2. 修复函数式组件的导航逻辑
确保useNavigation在NavigationContainer的子组件中使用,同时优化双因素认证触发逻辑:
import React from 'react'; import { View, Text, Button, StyleSheet } from 'react-native'; import { authorize } from 'react-native-app-auth'; import { useNavigation } from '@react-navigation/native'; const Login = () => { const navigation = useNavigation(); const config = { clientId: 'My_Client_ID', redirectUrl: 'exp://85-nny.anonymous.donewithit.exp.direct:80/--/auth', scopes: ['openid', 'profile', 'email', 'offline_access', 'User.Read'], serviceConfiguration: { authorizationEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', tokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token', }, // 强制触发双因素认证流程 additionalParameters: { prompt: 'select_account', }, }; const loginWithMicrosoft = async () => { try { const result = await authorize(config); if (result.idToken) { navigation.navigate('HomeScreen'); } } catch (error) { console.error('认证错误:', error); } }; return ( <View style={styles.container}> <Text>登录页面</Text> <Button title="使用Microsoft账户登录" onPress={loginWithMicrosoft} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, }); export default Login;
3. 修复类组件的导航逻辑
类组件需要通过withNavigation高阶组件包裹来获取导航props,同时补充双因素触发参数:
import React, { Component } from 'react'; import { View, Button, Text, StyleSheet } from 'react-native'; import { UserAgentApplication } from 'react-native-msal'; import { AuthenticationParameters } from 'react-native-msal'; import { withNavigation } from '@react-navigation/compat'; class Login extends Component { constructor(props) { super(props); this.auth = new UserAgentApplication({ auth: { clientId: 'My_Client_ID', authority: 'My_Authority', redirectUri: 'exp://85-nny.anonymous.donewithit.exp.direct:80/--/auth', }, }); this.state = { accessToken: null, userInfo: null, }; } login = async () => { try { const authResult = await this.auth.login({ scopes: ['openid', 'profile', 'email'], prompt: 'select_account', // 触发双因素认证 }); const accessToken = authResult.accessToken; if (accessToken) { this.setState({ accessToken }); this.getUserInfo(); this.props.navigation.navigate('HomeScreen'); } } catch (error) { console.error(error); } } getUserInfo = async () => { try { const authParameters = new AuthenticationParameters(); authParameters.scopes = ['openid', 'profile']; const userInfo = await this.auth.acquireTokenSilent(authParameters); this.setState({ userInfo }); } catch (error) { console.error(error); } } logout = () => { this.auth.logout(); this.setState({ accessToken: null, userInfo: null }); this.props.navigation.navigate('Login'); } render() { const { accessToken, userInfo } = this.state; return ( <View style={styles.container}> {accessToken ? ( <Button title="退出登录" onPress={this.logout} /> ) : ( <Button title="使用Microsoft账户登录" onPress={this.login} /> )} {userInfo && ( <View style={styles.userInfo}> <Text>欢迎, {userInfo.displayName}</Text> <Text>邮箱: {userInfo.userPrincipalName}</Text> </View> )} </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, userInfo: { marginTop: 20, alignItems: 'center', }, }); // 用withNavigation包裹以获取导航props export default withNavigation(Login);
额外注意事项
- 重定向URL有效性:当前使用的Expo开发URL仅适用于调试,正式发布时需要在Azure Portal的应用注册中配置对应平台的URI(iOS为
msauth://{BundleId}/{Base64编码的重定向URI},Android为msauth://{PackageName}/{Base64编码的重定向URI})。 - 双因素认证触发:通过添加
prompt: 'select_account'参数,确保Microsoft身份验证服务强制触发双因素认证流程。
内容的提问来源于stack exchange,提问作者Kim
相关产品推荐
相关产品推荐

