web-dev-qa-db-fra.com

withNavigation ne peut être utilisé que sur une hiérarchie de vues d'un navigateur

Je reçois l'erreur:

Violation invariante: withNavigation ne peut être utilisé que sur une hiérarchie de vues d'un navigateur. Le composant encapsulé ne peut pas accéder à la navigation à partir des accessoires ou du contexte

Je ne sais pas pourquoi, car j'utilise withNavigation dans d'autres composants de mon application et cela fonctionne. Je ne vois pas de différence entre les composants sur lesquels il fonctionne et celui qui provoque l'erreur.

Code:

le composant:

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)

le 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

Ici, je rend le StackNavigator qui est Menu dans mon composant d'application:

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

et mon composant racine:

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)

J'ai changé mon composant Vepo en ceci pour implémenter la réponse de 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

Aucune erreur, mais l'alerteModal ne s'affiche plus

7
BeniaminoBaggins

Dans react-navigation, le StackNavigator principal crée un fournisseur de contexte et le prop navigation sera mis à la disposition de tout composant en dessous de son niveau dans l'arborescence des composants s'ils utilisent le consommateur de contexte.

Deux façons d'accéder à l'accessoire navigation à l'aide du consommateur de contexte consiste à ajouter le composant à StackNavigator ou à utiliser la fonction withNavigation. Cependant, en raison du fonctionnement de l'API contextuelle de React, tout composant qui utilise la fonction withNavigation doit se trouver sous StackNavigator dans l'arborescence des composants.

Si vous souhaitez toujours accéder à la propriété navigation quelle que soit la position dans l'arborescence des composants, vous devrez stocker la référence dans le StackNavigator dans un module. Le guide suivant de react-navigation vous aidera à le faire https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html

10
vahissan

La réponse de Vahissan est correcte mais je n'ai pas pu le faire fonctionner en raison de diverses différences dans mon code de https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html comme mon stackNavigator n'étant pas un composant, c'est juste un objet.

Ce que j'ai réussi à faire était d'obtenir que le composant AlertModal soit un enfant du stackNavigator et de recevoir ainsi le prop navigation en l'ajoutant au ContentComponent de mon StackNavigator. Le code est comme ci-dessus mais dans le CustomDrawerContentComponent juste en le faisant comme ceci:

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>
)
0
BeniaminoBaggins