skip to Main Content

I implemented two navigators, AuthNavigator and AppNavigator. AuthNavigator contains a screen component that should navigate to screen component in AppNavigator. Since both are different I’m getting the error:

The action 'NAVIGATE' with payload {"name":"Feed"} was not handled by any navigator.

I Tried:

  • Nesting navigation but that generates and extra button on TabNavigator.
  • Passing the router trough props to QuestionScreen.
  • RefNavigation

without any luck. This is my current implementation.

AuthNavigator

import React from 'react'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

import LoginScreen from '../screens/LoginScreen'
import WelcomeScreen from '../screens/WelcomeScreen'
import RegisterScreen from '../screens/RegisterScreen'
import QuestionScreen from '../screens/questions/QuestionsScreen'

const Stack = createNativeStackNavigator()

const AuthNavigator = () => (
  <Stack.Navigator screenOptions={{ headerShown: false }}>
    <Stack.Screen name="Welcome" component={WelcomeScreen} />
    <Stack.Screen name="Login" component={LoginScreen} />
    <Stack.Screen name="Register" component={RegisterScreen} />
    <Stack.Screen name="Questions" component={QuestionScreen} />
  </Stack.Navigator>
)

export default AuthNavigator

AppNavigator

import React from 'react'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import { MaterialCommunityIcons } from '@expo/vector-icons'

import AccountScreen from '../screens/AccountScreen'
import CartScreen from '../screens/CartScreen'
import FeedNavigator from './FeedNavigator'
import CalendarButton from './CalendarButton'
import useNotifications from '../hooks/useNotifications'

const Tab = createBottomTabNavigator()

const AppNavigator = () => {

  return (
    <Tab.Navigator>
      <Tab.Screen 
        name="Account" 
        component={AccountScreen} 
        options={{
          tabBarIcon: ({ color, size }) => (
            <MaterialCommunityIcons name="menu" color={color} size={size} />
          )
        }}
      />
      <Tab.Screen 
        name="Feed" 
        component={FeedNavigator} 
        options={({
          navigation
        }) => ({
          tabBarButton: () => <CalendarButton onPress={() => navigation.navigate("Feed")} />,
          tabBarIcon: ({ color, size }) => (
            <MaterialCommunityIcons name="calendar-heart" color={color} size={size} />
          )
        })}
      />
      <Tab.Screen 
        name="Cart" 
        component={CartScreen} 
        options={{
          tabBarIcon: ({ color, size }) => (
            <MaterialCommunityIcons name="cart" color={color} size={size} />
          )
        }}
      />
    </Tab.Navigator>
  )}

export default AppNavigator

index.js

import React, { useCallback, useEffect, useState } from 'react'
import { NavigationContainer } from '@react-navigation/native'
import * as SplashScreen from 'expo-splash-screen'


import AppNavigator from './navigation/AppNavigator'
import AuthNavigator from './navigation/AuthNavigator'
import navigationTheme from './navigation/navigationTheme'
import AuthContext from './auth/context'
import authStorage from './auth/storage'
import useApi from './hooks/useApi'
import usersApi from './api/users'
import { navigationRef } from './navigation/rootNavigation'
import QuestionScreen from './screens/questions/QuestionsScreen'

SplashScreen.preventAutoHideAsync()


const App = () => {
  const profileApi = useApi(usersApi.getUserById)
  const [userId, setUserId] = useState()
  const [answersCompleted, setAnswersCompleted] = useState(false)
  const [appIsReady, setAppIsReady] = useState(false)
  const [error, setError] = useState()

  const getUserById = async (id) => {
    const result = await profileApi.request(id)

    if (!result.ok) {
      if (result.data) {
        setError(result.data.error)
      } else {
        setError("An expected error ocurred")
        console.log(result)
      }
      return
    }

    
    if (result.data.answers.length) {
      setAnswersCompleted(true)
    }
    
  }

  const restoreUserId = async () => {
    const user = await authStorage.getUserId()
    
    if (user) {
      setUserId(user._id)
      getUserById(user._id)
    }
  }

  useEffect(() => {
    async function prepare() {
      try {
        await restoreUserId()
      } catch (e) {
        console.warn(e);
      } finally {
        setAppIsReady(true);
      }
    }

    prepare();
  }, []);

  const onLayoutRootView = useCallback(async () => {
    if (appIsReady) {
      await SplashScreen.hideAsync();
    }
  }, [appIsReady]);
  
  if (!appIsReady) {
    return null;
  }

  return (
    <AuthContext.Provider value={{ userId, setUserId }} onLayout={onLayoutRootView}>
      <NavigationContainer ref={navigationRef} independent={true} theme={navigationTheme}>
        {userId && answersCompleted ? <AppNavigator /> : <AuthNavigator /> }
      </NavigationContainer>
    </AuthContext.Provider>
  )
}

export default App

QuestionScreen

import React, { useState, useCallback, useEffect } from 'react'
import { StyleSheet, View } from 'react-native'

import Breadcrumb from '../../components/breadcrumb'
import Screen from '../../components/Screen'
import Button from "../../components/Button"
import { QUESTIONS } from './questions'
import AppText from '../../components/Text'
import sizes from '../../config/sizes'
import answersApi from '../../api/answers'
import useAuth from '../../auth/useAuth'
import useApi from '../../hooks/useApi'
import { ErrorMessage } from '../../components/forms'
import colors from '../../config/colors'

const QuestionScreen = ({ navigation }) => {
  const saveAnswerApi = useApi(answersApi.save)
  const [currentIdxQuestion, setCurrentIdxQuestion] = useState(0)
  const [answer, setAnswer] = useState([])
  const [error, setError] = useState();
  const [answersSent, setAnswersSent] = useState(false)
  const auth = useAuth()

  const QuestionScreenContent = ({ idx }) => {

    const sendAnswers = async () => {
      const result = await saveAnswerApi.request(auth.userId, answer)
      
      if (!result.ok) {
        if (result.data) {
          setError(result.data.error)
        } else {
          setError("An expected error occurred")
          console.log(result)
        }
        return
      }


      navigation.navigate("Feed") // HERE IS THE ISSUE
    }

    const addAnswer = useCallback((response) => {
      const newAnswer = {
        id: answer.length + 1,
        question: QUESTIONS[idx].title,
        response
      };
  
      setAnswer((prevItems) => [...prevItems, newAnswer]);
      
      if (QUESTIONS.length > idx + 1) {
        setCurrentIdxQuestion(idx + 1)
      }
    }, [answer]);

    useEffect(() => {
      if (QUESTIONS.length === answer.length && !answersSent) {
        setAnswersSent(true)
        sendAnswers()
      }
    }, [currentIdxQuestion, answersSent])

    return (
      <View style={styles.content}>
        <AppText style={styles.title}>{QUESTIONS[idx].title}</AppText>
        <AppText style={styles.subtitle}>{QUESTIONS[idx].subtitle}</AppText>

        {QUESTIONS[idx].options.map((question) => {
          return (
            <Button
              key={question.id}
              title={question.title}
              color="white"
              onPress={() => addAnswer(question.title)}
            />
          )
        })}
      </View>
    )
  }

  return (
    <Screen style={styles.container}>
      <Breadcrumb crumbs={QUESTIONS} currentIdxQuestion={currentIdxQuestion}/>
      <ErrorMessage error={error} visible={error} />
      <QuestionScreenContent idx={currentIdxQuestion} />
    </Screen>
  )
}

const styles = StyleSheet.create({
  container: {
    padding: 10,
  },
  content: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    paddingHorizontal: 20,
  },
  title: {
    fontSize: 22,
    marginBottom: 60,
    paddingHorizontal: 45,
    fontWeight: 'bold',
    color: colors.primarySoft,
  },
  subtitle: {
    fontSize: sizes.small,
    textAlign: 'center',
  },
})

export default QuestionScreen

Any idea to solve this issue?

2

Answers


  1. Chosen as BEST ANSWER

    I fixed my issue adding 'answerCompleted' variable to my AuthContext, this is my new index.js:

    <AuthContext.Provider value={{ userId, setUserId, answersCompleted, setAnswersCompleted }} onLayout={onLayoutRootView}> ... </AuthContext.Provider>
    

    Also I added more login on my context in order to update "answerCompleted" variable.


  2. Try using this function in QuestionScreen.

     navigation.navigate('AppNavigator', { screen: 'Feed' });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search