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

iPad SplitViewController 带有用于详细视图的单独导航堆栈

想要对导航栏显示透明

Xcode:用于在提交视图中导航更改的键盘快捷键?