web-dev-qa-db-ja.com

withNavigationは、ナビゲーターのビュー階層でのみ使用できます

エラーが発生しています:

不変違反: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)

Vahissanによる回答を実装するために、Vepoコンポーネントをこれに変更しました。

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は表示されなくなりました

7

反応ナビゲーションでは、メインのStackNavigatorがコンテキストプロバイダーを作成し、コンテキストコンシューマーを使用する場合、navigationプロパティがコンポーネントツリー内のそのレベルより下のすべてのコンポーネントで使用可能になります。

コンテキストコンシューマを使用してnavigationプロパティにアクセスする2つの方法は、StackNavigatorにコンポーネントを追加するか、withNavigation関数を使用することです。ただし、ReactのコンテキストAPIの動作方法のため、withNavigation関数を使用するコンポーネントは、コンポーネントツリーのStackNavigatorの下になければなりません。

コンポーネントツリー内の位置に関係なくnavigation propにアクセスしたい場合は、モジュール内のStackNavigatorへの参照を保存する必要があります。 react-navigationのガイドに従うと、そのために役立ちます https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html

10
vahissan

Vahissanの答えは正しいですが、 https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html と私のstackNavigatorのようなコードのさまざまな違いのため、私はそれを動作させることができませんでしたコンポーネントではなく、単なるオブジェクトです。

どうにかしてAlertModalコンポーネントをstackNavigatorの子にし、navigationのContentComponentに追加することでStackNavigator propを受け取りました。コードは上記のとおりですが、CustomDrawerContentComponentをintにすると、次のようになります。

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