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
I fixed my issue adding 'answerCompleted' variable to my AuthContext, this is my new index.js:
Also I added more login on my context in order to update "answerCompleted" variable.
Try using this function in QuestionScreen.