0

I'm following this SO answer - I'm just trying to reset the stack upon navigating so that my component gets unmounted when navigating away because I need componentDidMount to fire when navigating to it.

I want to go from this:

const navigateAction = NavigationActions.navigate({
  routeName: route
})
props.navigation.dispatch(navigateAction)

to this:

const resetAction = StackActions.reset({
  index: 0,
  actions: [NavigationActions.navigate({ routeName: route })],
})
props.navigation.dispatch(resetAction)

But when I do I get this error:

Error: There is no route defined for key Add Grocery Item. Must be one of: 'screen' stackactions

This is the full function which the code above is in:

  navigateToScreen = (route, props, headingKey: string) => () => {
    if (this.props.navigationProps.activeItemKey !== headingKey) {
      this.setState({ activeSections: [] })
    }
    // const navigateAction = NavigationActions.navigate({
    //   routeName: route
    // })
    
    const resetAction = StackActions.reset({
      index: 0,
      actions: [NavigationActions.navigate({ routeName: route })],
    })
    props.navigation.dispatch(resetAction)
  }

Here are my navigation routes:

export const AddMenuIcon = ({ navigate }: NavigateType) => (
  <View>
    <Icon
      name="plus"
      size={25}
      color="#FFF"
      onPress={() => navigate('DrawerOpen')}
    />
  </View>
)

export const SearchMenuIcon = ({ navigate }: NavigateType) => (
  <Icon
    name="search"
    size={25}
    color="#FFF"
    onPress={() => navigate('DrawerOpen')}
  />
)

export const Stack = {
  Login: {
    screen: Login
  },
  Search: {
    screen: Search
  },
  AddGroceryItem: {
    screen: AddGroceryItem
  },
  AddGroceryStore: {
    screen: AddGroceryStore
  }
}

export const DrawerRoutes = {
  Login: {
    name: 'Login',
    screen: Login
  },
  'Find Vegan': {
    name: 'Search',
    screen: createStackNavigator(Stack.Search, {
      headerMode: 'none'
    }),
    navigationOptions: ({ navigation }: NavigationType) => ({
      drawerIcon: SearchMenuIcon(navigation)
    })
  },
  'Add Grocery Item': {
    name: 'Add',
    screen: createStackNavigator(Stack.AddGroceryItem, {
      headerMode: 'none'
    }),
    navigationOptions: ({ navigation }: NavigationType) => ({
      drawerIcon: AddMenuIcon(navigation)
    })
  },
  'Add Grocery Store': {
    name: 'AddGroceryStore',
    screen: createStackNavigator(Stack.AddGroceryStore, {
      headerMode: 'none'
    }),
    navigationOptions: ({ navigation }: NavigationType) => ({
      drawerIcon: AddMenuIcon(navigation)
    })
  }
}

export const sections = [
  {
    mainHeading: 'Login',
    navigationPath: 'Login',
    icon: 'user-circle'
  },
  {
    mainHeading: 'Find Vegan',
    navigationPath: 'Find Vegan',
    icon: 'search'
  },
  {
    mainHeading: 'Add Vegan',
    subOptions: [
      {
        secondaryHeading: 'Grocery Items',
        mainHeading: 'Add Vegan',
        secondaryHeadingKey: 'Add Grocery Item',
        navigationPath: 'Add Grocery Item',
        icon: { name: 'shopping-basket', color: Colors.white }
      },
      {
        secondaryHeading: 'Grocery Stores',
        mainHeading: 'Add Vegan',
        secondaryHeadingKey: 'Add Grocery Itemx',
        navigationPath: 'Add Grocery Store',
        icon: { name: 'shopping-cart', color: Colors.white }
      },
      {
        secondaryHeading: 'Restaurant Meals',
        mainHeading: 'Add Vegan',
        secondaryHeadingKey: 'Add Grocery Itemx',
        navigationPath: 'Add Grocery Item',
        icon: { name: 'utensils', color: Colors.white }
      },
      {
        secondaryHeading: 'Restaurants',
        mainHeading: 'Add Vegan',
        secondaryHeadingKey: 'Add Grocery Itemx',
        navigationPath: 'Add Grocery Item',
        icon: { name: 'store-alt', color: Colors.white }
      },
      {
        secondaryHeading: 'Events',
        mainHeading: 'Add Vegan',
        secondaryHeadingKey: 'Add Grocery Itemx',
        navigationPath: 'Add Grocery Item',
        icon: { name: 'calendar-alt', color: Colors.white }
      },
      {
        secondaryHeading: 'Fashion',
        mainHeading: 'Add Vegan',
        secondaryHeadingKey: 'Add Grocery Itemx',
        navigationPath: 'Add Grocery Item',
        icon: { name: 'tshirt', color: Colors.white }
      }
    ],
    icon: 'plus-circle'
  }
]

Here is my full menu component which has the naviagtion menu

class AccordionView extends React.Component<AccordianProps, AccordianState> {
  state = {
    activeSections: [],
    sections: Data.sections
  }

  getParentLabelStyle = (mainHeading: string) => {
    if (this.props.navigationProps.activeItemKey === mainHeading) {
      return { ...Typography.whiteLabel, color: Colors.green_lite_2 }
    }
    const sectionFromSubOption = this.state.sections.find(
      (x) =>
        x.subOptions &&
        x.subOptions.find(
          (y) =>
            y.secondaryHeadingKey === this.props.navigationProps.activeItemKey
        )
    )
    if (
      sectionFromSubOption &&
      sectionFromSubOption.mainHeading === mainHeading
    ) {
      return { ...Typography.whiteLabel, color: Colors.green_lite_2 }
    } else {
      return Typography.whiteLabel
    }
  }

  getParentIconColor = (mainHeading: string) => {
    if (this.props.navigationProps.activeItemKey === mainHeading) {
      return Colors.green_lite_2
    }
    const sectionFromSubOption = this.state.sections.find(
      (x) =>
        x.subOptions &&
        x.subOptions.find(
          (y) =>
            y.secondaryHeadingKey === this.props.navigationProps.activeItemKey
        )
    )
    if (
      sectionFromSubOption &&
      sectionFromSubOption.mainHeading === mainHeading
    ) {
      return Colors.green_lite_2
    } else {
      return Colors.white
    }
  }

  getLabelStyle = (headingKey: string) => {
    if (this.props.navigationProps.activeItemKey === headingKey) {
      return { ...Typography.whiteLabel, color: Colors.green_lite_2 }
    } else {
      return Typography.whiteLabel
    }
  }

  getIconColor = (headingKey: string): string => {
    if (this.props.navigationProps.activeItemKey === headingKey) {
      return Colors.green_lite_2
    } else {
      return Colors.white
    }
  }

  updateSections = (activeSections) => {
    this.setState({ activeSections })
  }

  navigateToScreen = (route, props, headingKey: string) => () => {
    if (this.props.navigationProps.activeItemKey !== headingKey) {
      this.setState({ activeSections: [] })
    }
    // const navigateAction = NavigationActions.navigate({
    //   routeName: route
    // })
    
    const resetAction = StackActions.reset({
      index: 0,
      actions: [NavigationActions.navigate({ routeName: route })],
    })
    props.navigation.dispatch(resetAction)
  }

  renderSectionTitle = () => {
    return <View />
  }

  renderHeader = (section, userName?: string) => {
    return (
      <View>
        {section.navigationPath && (
          <TouchableOpacity
            onPress={this.navigateToScreen(
              section.navigationPath,
              this.props.navigationProps
            )}>
            <View style={styles.subMenuItemContainer}>
              <AwesomeIcon
                style={styles.icon}
                name={section.icon}
                light
                size={25}
                color={this.getParentIconColor(section.mainHeading)}
              />
              <Text
                style={{ ...this.getParentLabelStyle(section.mainHeading) }}>
                {userName ? userName : section.mainHeading}
              </Text>
            </View>
          </TouchableOpacity>
        )}
        {section.subOptions && (
          <View style={styles.subMenuItemContainer}>
            <AwesomeIcon
              style={styles.icon}
              name={section.icon}
              size={25}
              light
              color={this.getParentIconColor(section.mainHeading)}
            />
            <Text style={{ ...this.getParentLabelStyle(section.mainHeading) }}>
              {section.mainHeading}
            </Text>
          </View>
        )}
      </View>
    )
  }

  renderContent = (section) => {
    return (
      section.subOptions &&
      section.subOptions.map(
        (item) =>
          item.secondaryHeading === 'Grocery Items' ? (
            <TouchableOpacity
              onPress={this.navigateToScreen(
                item.navigationPath,
                this.props.navigationProps,
                item.secondaryHeadingKey
              )}>
              <View style={{...styles.parentMenuItemContainer, marginLeft: -20}}>
                <AwesomeIcon
                  style={styles.icon}
                  size={25}
                  light
                  name={item.icon.name}
                  color={this.getIconColor(item.secondaryHeadingKey)}
                />
                <Text
                  style={{ ...this.getLabelStyle(item.secondaryHeadingKey) }}>
                  {item.secondaryHeading}
                </Text>
              </View>
            </TouchableOpacity>
          ) : (
            <View style={{...styles.parentMenuItemContainer, marginLeft: -20}}>
              <AwesomeIcon
                style={styles.icon}
                size={25}
                light
                name={item.icon.name}
                color={'#969696'}
              />
              <Text
                style={{ ...this.getLabelStyle(item.secondaryHeadingKey), color: '#969696' }}>
                {item.secondaryHeading}
              </Text>
              <Text
                style={{ ...this.getLabelStyle(item.secondaryHeadingKey), fontSize: 12, paddingLeft: 0, color: 'red' }}>
                (coming soon)
              </Text>
            </View>

          )
      )
    )
  }

  render() {
    return (
      <Accordion
        sections={this.state.sections}
        activeSections={this.state.activeSections}
        renderSectionTitle={this.renderSectionTitle}
        renderHeader={(section) => {
          let userName
          if (section.mainHeading === 'Login') {
            const loginItem = this.props?.navigationProps?.items?.find(
              (x) => x.key === 'Login'
            )
            userName = loginItem?.params?.userFirstName
          }
          return this.renderHeader(section, userName)
        }}
        renderContent={this.renderContent}
        onChange={this.updateSections}
      />
    )
  }
}

export class CustomDrawerContentComponent extends React.Component<
  AccordianProps,
  AccordianState
> {
  state = {}
  render() {
    return (
      <SafeAreaView style={styles.menuContainer}>
        <View style={styles.vepoImageContainer}>
          <Image
            style={styles.vepoImage}
            square
            source={require('src/images/logo_v_white.png')}
          />
        </View>

        <Text style={styles.mottoText}>Find Every Vegan Thing!</Text>
        <View style={styles.accordianContainer}>
          <AccordionView navigationProps={this.props} />
        </View>

        <AlertModalComponent
          yesClicked={() => {
            updateAlertModalIsOpen(false)
          }}
        />
      </SafeAreaView>
    )
  }
}
const { width, height } = Dimensions.get('screen')

let Menu = createStackNavigator(
  {
    Drawer: {
      name: 'Drawer',
      screen: createDrawerNavigator(Data.DrawerRoutes, {
        initialRouteName: 'Login',
        drawerPosition: 'left',
        drawerWidth: Math.min(height, width),
        contentComponent: CustomDrawerContentComponent,
        contentOptions: {
          activeTintColor: '#27a562',
          inactiveTintColor: 'white',
          activeBackgroundColor: '#3a3a3a'
        }
      })
    }
  },
  {
    headerMode: 'none',
    initialRouteName: 'Drawer'
  }
)

const menuItemContainer = {
  display: 'flex',
  flexDirection: 'row',
  height: 50,
  alignItems: 'center',
  borderBottomWidth: Borders.thinBorder,
  borderBottomColor: Colors.grey_dk_2
}

const styles = {
  vepoImage: {
    marginLeft: Spacing.sm_4,
    marginBottom: Spacing.none,
    marginTop: Spacing.md_2,
    width: 120,
    height: Distances.FormElementHeights.Double,
    resizeMode: 'contain'
  },
  accordianContainer: {
    borderTopWidth: Borders.thinBorder,
    borderTopColor: Colors.grey_dk_2
  },
  mottoText: { ...Typography.whiteLabel, marginBottom: Spacing.md_4 },
  vepoImageContainer: {
    marginBottom: Spacing.md_4
  },
  menuContainer: {
    flex: 1,
    backgroundColor: Colors.grey_dk_3,
    color: Colors.white
  },
  icon: {
    marginLeft: Spacing.md_4,
    width: 30
  },
  subMenuItemContainer: menuItemContainer,
  parentMenuItemContainer: {
    ...menuItemContainer,
    paddingLeft: Spacing.lg_7
  }
}

// $FlowFixMe
Menu = createAppContainer(Menu)

export default Menu

How do I fix this error?

BeniaminoBaggins
  • 11,202
  • 41
  • 152
  • 287

1 Answers1

0

Looks like you're navigating to the navigationPath property of each section, which in this case you've defined as Add Grocery Item, whereas the actual path that you're trying to navigate to is AddGroceryItem.

Kai
  • 2,529
  • 1
  • 15
  • 24
  • Thank you. To test your theory I tried hardcoding it: `actions: [NavigationActions.navigate({ routeName: 'AddGroceryItem' })]` and got "Error: There is no route defined for key AddGroceryItem. Must be one of: 'screen'". So I don't think so (If I understood you correctly). – BeniaminoBaggins Jul 23 '20 at 03:11