withNavigation 只能用于导航器的视图层次结构
Posted
技术标签:
【中文标题】withNavigation 只能用于导航器的视图层次结构【英文标题】:withNavigation can only be used on a view hierarchy of a navigator 【发布时间】:2019-03-18 07:12:07 【问题描述】:我收到了错误:
Invariant Violation:withNavigation 只能用于视图 导航器的层次结构。被包裹的组件无法获取 从道具或上下文访问导航
我不知道为什么,因为我在我的应用程序的其他组件中使用了withNavigation
,它可以工作。我看不出它所作用的组件与导致错误的组件有什么不同。
代码:
组件:
const mapStateToProps = (state: State): Object => (
alertModal: state.formControls.alertModal
)
const mapDispatchToProps = (dispatch: Dispatch<*>): Object =>
return bindActionCreators(
updateAlertModalHeight: updateAlertModalHeight,
updateAlertModalIsOpen: updateAlertModalIsOpen,
updateHasYesNo: updateAlertModalHasYesNo
,
dispatch
)
class AlertModalView extends Component<AlertModalProps, State>
render(): Node
return (
<View style=alertModalStyle.container>
<PresentationalModal
style=presentationalModalStyle
isOpen=this.props.alertModal.isOpen
title=this.props.alertModal.title
navigation=this.props.navigation
updateHasYesNo=this.props.updateHasYesNo
message=this.props.alertModal.message
updateAlertModalHeight=this.props.updateAlertModalHeight
viewHeight=this.props.alertModal.viewHeight
hasYesNo=this.props.alertModal.hasYesNo
yesClicked=this.props.alertModal.yesClicked
updateAlertModalIsOpen=this.props.updateAlertModalIsOpen
/>
</View>
)
// $FlowFixMe
const AlertModalViewComponent = connect(
mapStateToProps,
mapDispatchToProps
)(AlertModalView)
export default withNavigation(AlertModalViewComponent)
stackNavigator:
import React from 'react'
import View, SafeAreaView from 'react-native'
import Icon from 'react-native-vector-icons/EvilIcons'
import Add from '../product/add/view'
import Login from '../user/login/view'
import Search from '../product/search/query/view'
import Image from 'react-native'
import StackNavigator, DrawerNavigator, DrawerItems from 'react-navigation'
const AddMenuIcon = ( navigate ) => (
<View>
<Icon
name="plus"
size=30
color="#FFF"
onPress=() => navigate('DrawerOpen')
/>
</View>
)
const SearchMenuIcon = ( navigate ) => (
<Icon
name="search"
size=30
color="#FFF"
onPress=() => navigate('DrawerOpen')
/>
)
const Stack =
Login:
screen: Login
,
Search:
screen: Search
,
Add:
screen: Add
const DrawerRoutes =
Login:
name: 'Login',
screen: Login
,
'Search Vegan':
name: 'Search',
screen: StackNavigator(Stack.Search,
headerMode: 'none'
),
navigationOptions: ( navigation ) => (
drawerIcon: SearchMenuIcon(navigation)
)
,
'Add vegan':
name: 'Add',
screen: StackNavigator(Stack.Add,
headerMode: 'none'
),
navigationOptions: ( navigation ) => (
drawerIcon: AddMenuIcon(navigation)
)
const CustomDrawerContentComponent = props => (
<SafeAreaView style= flex: 1, backgroundColor: '#3f3f3f', color: 'white' >
<View>
<Image
style=
marginLeft: 20,
marginBottom: 0,
marginTop: 0,
width: 100,
height: 100,
resizeMode: 'contain'
square
source=require('../../images/logo_v_white.png')
/>
</View>
<DrawerItems ...props />
</SafeAreaView>
)
const Menu = StackNavigator(
Drawer:
name: 'Drawer',
screen: DrawerNavigator(DrawerRoutes,
initialRouteName: 'Login',
drawerPosition: 'left',
contentComponent: CustomDrawerContentComponent,
contentOptions:
activeTintColor: '#27a562',
inactiveTintColor: 'white',
activeBackgroundColor: '#3a3a3a'
)
,
headerMode: 'none',
initialRouteName: 'Drawer'
)
export default Menu
在这里,我在我的应用组件中呈现StackNavigator
,即Menu
:
import React, Component from 'react'
import Menu from './menu/view'
import Props from 'prop-types'
import Container from 'native-base'
import updateAlertModalIsOpen from './formControls/alertModal/action'
import AlertModalComponent from './formControls/alertModal/view'
import UserLoginModal from './user/login/loginModal/view'
class Vepo extends Component
componentDidMount()
const store = this.context
this.unsubscribe = store.subscribe(() => )
store.dispatch(this.props.fetchUserGeoCoords())
store.dispatch(this.props.fetchSearchQueryPageCategories())
store.dispatch(this.props.fetchCategories())
componentWillUnmount()
this.unsubscribe()
render(): Object
return (
<Container>
<Menu store=this.context />
<AlertModalComponent
yesClicked=() =>
updateAlertModalIsOpen(false)
/>
<UserLoginModal />
</Container>
)
Vepo.contextTypes =
store: Props.object
export default Vepo
和我的根组件:
export const store = createStore(
rootReducer,
vepo,
composeWithDevTools(applyMiddleware(createEpicMiddleware(rootEpic)))
)
import NavigationService from './navigationService'
export const App = () => (
<Provider store=store>
<Vepo
fetchUserGeoCoords=fetchUserGeoCoords
fetchSearchQueryPageCategories=fetchSearchQueryPageCategories
fetchCategories=fetchCategories
/>
</Provider>
)
AppRegistry.registerComponent('vepo', () => App)
我已将我的 Vepo 组件更改为此以实现 vahissan 的答案:
import React, Component from 'react'
import Menu from './menu/view'
import Props from 'prop-types'
import Container from 'native-base'
import updateAlertModalIsOpen from './formControls/alertModal/action'
import AlertModalComponent from './formControls/alertModal/view'
import UserLoginModal from './user/login/loginModal/view'
import NavigationService from './navigationService'
class Vepo extends Component
componentDidMount()
const store = this.context
this.unsubscribe = store.subscribe(() => )
store.dispatch(this.props.fetchUserGeoCoords())
store.dispatch(this.props.fetchSearchQueryPageCategories())
store.dispatch(this.props.fetchCategories())
componentWillUnmount()
this.unsubscribe()
render(): Object
return (
<Container>
<Menu
store=this.context
ref=navigatorRef =>
NavigationService.setTopLevelNavigator(navigatorRef)
>
<AlertModalComponent
yesClicked=() =>
updateAlertModalIsOpen(false)
/>
</Menu>
<UserLoginModal />
</Container>
)
Vepo.contextTypes =
store: Props.object
export default Vepo
没有错误,但 alertModal 不再显示
【问题讨论】:
你在哪里渲染你的组件 wrt StackNavigator? @vahissan 我刚刚将其发布到问题的底部。干杯 StackNavigator 和您的组件处于同一级别。这就是你得到错误的原因。您只能在 StackNavigator 的子组件中呈现的组件中使用 withNavigation 函数。 这将帮助你实现你想要的reactnavigation.org/docs/en/… 【参考方案1】:在 react-navigation 中,主 StackNavigator 创建一个上下文提供者,如果组件树中低于其级别的任何组件使用上下文使用者,则 navigation
属性将可用。
使用上下文使用者访问 navigation
属性的两种方法是将组件添加到 StackNavigator 或使用 withNavigation
函数。然而,由于 React 的上下文 API 的工作方式,任何使用 withNavigation
函数的组件都必须位于组件树中的 StackNavigator 下方。
如果您仍然想访问 navigation
属性而不管组件树中的位置如何,您必须将 ref 存储到模块中的 StackNavigator 中。遵循 react-navigation 的指南将帮助您做到这一点https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
【讨论】:
谢谢。这给了我正确的道路。当我引入导航服务时,我的应用程序停止呈现任何屏幕。我一定做得不对。这将如何适应我的根组件?我刚刚将我真正的根组件添加到问题中。 我已将我的尝试添加到问题的底部【参考方案2】:如果您使用的是 react-navigation 版本 5,请使用带有功能组件的 useNavigation 挂钩。这个钩子将导航对象注入到功能组件中。这是文档的链接:
https://reactnavigation.org/docs/use-navigation/
【讨论】:
完美运行,谢谢。正在大量寻找这个。 谢谢!尽管有一个类组件作为我的一个 StackNavigator 屏幕的深层嵌套子项(根据 @vahissan 的建议),但我无法使用withNavigation
克服这个错误。我将我的类组件更改为功能组件,并毫无问题地使用了useNavigation
钩子。不必在我的组件树中移动任何东西。【参考方案3】:
Vahissan 的回答是正确的,但由于我的代码与 https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html 的各种差异,我无法让它工作,就像我的 stackNavigator 不是一个组件,它只是一个对象。
我设法让AlertModal
组件成为stackNavigator 的子组件,从而通过将navigation
属性添加到我的StackNavigator
的ContentComponent 中来接收它。代码如上,但 CustomDrawerContentComponent
只是这样:
const CustomDrawerContentComponent = props => (
<SafeAreaView style= flex: 1, backgroundColor: '#3f3f3f', color: 'white' >
<View>
<Image
style=
marginLeft: 20,
marginBottom: 0,
marginTop: 0,
width: 100,
height: 100,
resizeMode: 'contain'
square
source=require('../../images/logo_v_white.png')
/>
</View>
<DrawerItems ...props />
<AlertModalComponent
yesClicked=() =>
updateAlertModalIsOpen(false)
/>
</SafeAreaView>
)
【讨论】:
【参考方案4】:对于任何可能感兴趣的人,您需要 npm install @react-navigation/native @react-navigation/compat @react-navigation/stack 以使 withNavigation 在最新版本中工作
【讨论】:
以上是关于withNavigation 只能用于导航器的视图层次结构的主要内容,如果未能解决你的问题,请参考以下文章
使用 Navigator.pushNamed 导航时如何发送参数
如何在打字稿中使用 react-navigation 的 withNavigation?
react-navigation withNavigation 返回 undefined