React Navigation v4x在AppContainer外导航报错,求解决跳转Login页面问题
我来帮你搞定这个undefined is not an object (navigation.dispatch)的错误!
错误原因分析
你当前的代码里,当isFirstLaunch为true时,只渲染了<EntryInfo />而没有渲染<AppContainer />——这就导致this.navigator根本没有被赋值(因为ref回调只有在AppContainer渲染时才会执行),所以调用this.navigator.dispatch自然会抛出undefined的错误。React Navigation的导航实例完全依赖AppContainer的存在,没有它就没有可用的导航上下文。
解决方案一:将EntryInfo纳入导航栈(推荐)
这种方式更符合React Navigation的设计逻辑,让AppContainer始终渲染,通过初始路由控制显示内容:
创建全局导航服务
新建一个NavigationService.js文件,用来保存导航引用,方便在任何组件中调用:import { NavigationActions } from 'react-navigation'; let _navigator; export function setTopLevelNavigator(navigatorRef) { _navigator = navigatorRef; } export function navigate(routeName, params) { _navigator.dispatch( NavigationActions.navigate({ routeName, params, }) ); } export default { navigate, setTopLevelNavigator, };更新导航栈配置
把EntryInfo添加到你的导航栈中:import { createStackNavigator } from 'react-navigation-stack'; import EntryInfo from './path/to/EntryInfo'; import Login from './path/to/Login'; // 导入其他页面 const AppStack = createStackNavigator({ EntryInfo: { screen: EntryInfo }, Login: { screen: Login }, // 其他路由 }); const AppContainer = createAppContainer(AppStack);修改App组件逻辑
让AppContainer始终渲染,根据isFirstLaunch的值设置初始页面:import NavigationService from './NavigationService'; export default class App extends Component { constructor(props) { super(props); this.state = { isFirstLaunch: false, hasCheckedAsyncStorage: false }; } async componentDidMount() { const isFirstLaunch = await checkIfFirstLaunch(); this.setState({ isFirstLaunch, hasCheckedAsyncStorage: true }, () => { // 状态更新后跳转到对应的初始页面 const initialRoute = isFirstLaunch ? 'EntryInfo' : 'Login'; NavigationService.navigate(initialRoute); }); } render() { const { hasCheckedAsyncStorage } = this.state; if (!hasCheckedAsyncStorage) { return null; // 可以换成加载动画提升体验 } return ( <AppContainer ref={nav => NavigationService.setTopLevelNavigator(nav)} /> ); } }修改EntryInfo组件的跳转逻辑
在EntryInfo里直接调用导航服务即可:import NavigationService from './NavigationService'; // 比如在按钮点击事件中 <Button title="去登录" onPress={() => NavigationService.navigate('Login')} />
解决方案二:强制AppContainer始终渲染(不推荐,仅作备选)
如果你不想把EntryInfo加入导航栈,可以让AppContainer始终渲染,用状态控制EntryInfo的显示,并在跳转前确保AppContainer已初始化:
export default class App extends Component { constructor(props) { super(props); this.state = { isFirstLaunch: false, hasCheckedAsyncStorage: false }; // 绑定方法到当前实例 this.onPressLogin = this.onPressLogin.bind(this); } onPressLogin() { // 先切换状态渲染AppContainer,再触发导航 this.setState({ isFirstLaunch: false }, () => { this.navigator.dispatch( NavigationActions.navigate({ routeName: 'Login' }) ); }); } async componentDidMount() { const isFirstLaunch = await checkIfFirstLaunch(); this.setState({ isFirstLaunch, hasCheckedAsyncStorage: true }); } render() { const { hasCheckedAsyncStorage, isFirstLaunch } = this.state; if (!hasCheckedAsyncStorage) { return null; } return ( <> {/* 用绝对定位让EntryInfo覆盖在AppContainer上 */} {isFirstLaunch && ( <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}> <EntryInfo onPress={this.onPressLogin} /> </View> )} <AppContainer ref={nav => { this.navigator = nav; }} /> </> ); } }
这种方式需要处理布局覆盖的问题,不如第一种方案优雅,所以优先推荐方案一。
内容的提问来源于stack exchange,提问作者user6533295
相关产品推荐
相关产品推荐

