React Native Error: The action 'NAVIGATE' with payload {"name":"GameScreen"} was not handled by any navigator - react-native

I know that this issue has been brought up before but I have not found any answer that works for me.
I simply want to navigate from screen "Start" to screen "Game", using App.js as the router.
App.js:
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Start">
<Stack.Screen name="Start" component={StartingScreen} />
<Stack.Screen name="Game" component={GameScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
StartingScreen.js:
const StartingScreen = ({ navigation }) => {
//lots of code
return (
//more code
<Button title="Begin" onPress={() => {navigation.navigate('GameScreen')}}/>
)
This gives me the error in the title with ("Do you have a game named 'GameScreen'?") and nothing happens. I have tried following the React Navigation docs, but in their example they put everything in App.js, that does not work for me. Other things I have tried include exporting the navigation stack to StartingScreen.js, changing the arguments of navigation.navigate(), placing the navigator inside StartingScreen.js.
GameScreen is spelled exactly the same in all places.

Change your code like below
const StartingScreen = ({ navigation }) => {
//lots of code
return (
//more code
<Button title="Begin" onPress={() => {navigation.navigate('Game')}}/>
)
You are giving the component name but you should provide the name of the screen that you give in the stack which is 'Game'

As your screen name is given 'Game' in the stack navigator, you should navigate to 'Game' instead of 'GameScreen'.
onPress={() => {navigation.navigate('Game')}}

Related

How to add screen at beginnig with CommonsActions.reset() in react navigation

I want to change screens order in navigation flow reseting navigation state
I want to change screens order in navigation flow, something like
{
condition ? <> <Screen A/> <Screen B/> </> : <> <Screen B/> <Screen A/> </>
}
following [https://reactnavigation.org/docs/auth-flow/#removing-shared-screens-when-auth-state-changes%5C]
How is the same screen this dont change the order (only when get condition from storage when app starts), so i was trying something like this to add screen A at beginnig keeping the others
navigation.dispatch((state) => {
const stateCopy = { ...state };
const routes = stateCopy?.routes?.unshift({ name: 'screen-A' });
// screen a dont have the key as the others in state
return CommonActions.reset({
...state,
routes,
index: 0,
});
});
Basically this doesnt works and i didnt find an example in [https://reactnavigation.org/docs/navigation-actions/#reset%5C] . How can i do this? Thanks.

React Native: How to handle protected routes with react navigation

What I want is to protect specific screens where the user can run specific actions and only at that point redirect him to a login screen, if the user is successfully authenticated, redirect him to the point where he left off the flow.
We can do this in React JS(with react-router), so I would like to know if its posible to implement a similar solution for react-native(with react-navigation):
React JS approach with React Router
function RequireAuth({ children }) {
const { authed } = useAuth();
const location = useLocation();
return authed === true ? (
children
) : (
<Navigate to="/login" replace state={{ path: location.pathname }} />
);
}
This is possible in react native and is documented in the authentication workflow section of the documentation.
In summary we conditionally need to render screens inside the navigator. Here is a minimal example using a Stack.Navigator which is similar to your posted code snippet. The workflow is identical for other navigators (nested or not). I will use the context api in my example, but there are other ways to achieve the same as well. The overall pattern is the same.
const AppContext = React.createContext()
function App() {
const [isSignedIn, setIsSignedIn] = React.useState(false)
const appContextValue = useMemo(
() => ({
isSignedIn,
setIsSignedIn,
}),
[isSignedIn]
)
return (
<AppContext.Provider value={appContextValue}>
<Stack.Navigator>
{!isSignedIn ? (
<Stack.Screen
name="Login"
component={LoginScreen}
/>
) : (
// whatever screens if user is logged in
<Stack.Screen name="Home" component={HomeScreen} />
)}
</Stack.Navigator>
</AppContext.Provider>
);
In the LoginScreen, you then need to change the authed state to true if the login was successful. The first screen in the conditional will then be initially rendered automatically (in this case HomeScreen).
function LoginScreen() {
const { setIsSignedIn } = useContext(AppContext)
// do whatever, then setIsSignedIn to true
}

Expo v4 / React : Passing datas between tab screen

Expo v4..
Hello friends,
I am using expo tab navigation:
https://reactnavigation.org/docs/hello-react-navigation
How can I pass datas bewteen expo tabs ?
OrderScreen.tsx:
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs';
const Tab = createMaterialTopTabNavigator();
export default function OrderScreen({navigation, route}) {
let data = route.params
return (
<Tab.Navigator>
<Tab.Screen name="Details" component={DetailsScreen} />
<Tab.Screen name="Photos" component={PhotosScreen} />
</Tab.Navigator>
);
}
PhotosScreen.tsx:
export default function PhotosScreen() {
// HOW TO GET route.params here ??
DetailScreen.tsx:
export default function DetailsScreen() {
// HOW TO GET route.params here ??
I manage to have data in only one tab using :
navigation.navigate('Order', {screen: 'Details' ,params: {datas:someDatas}});
But I want to have the datas in both tabs.
I have been searching everywhere but impossible to find.
I would be very surprise there is no solution for that.
T H A N K S !!!! :-)
finding no answer on the net, I've been using the context api.
for a guy new to react it took a while to get the idea of context but it seems to be quite powerful once settled.

access `headerRight` inside a screen that is hosted by stack navigator

I have created a stack navigator:
import {createStackNavigator} from '#react-navigation/stack';
const TheStack = createStackNavigator();
Then, This is my navigator, it claimed component={LandingScreen}:
<TheStack.Navigator ...>
<TheStack.Screen
name="LandingScreen"
component={LandingScreen}
options={{
title: '',
headerLeft: null,
headerRight: () => (
<MyHeaderRightComponent />
),
}}
/>
<TheStack.Navigator>
As you can see above in options of the screen, there is headerRight, I have declared using MyHeaderRightComponent as headerRight so that it is shown on the right side of the header on screen.
Here is my LandingScreen.js :
import React, {useState} from 'react';
import {View, StyleSheet} from 'react-native';
const LandingScreen = ({navigation}) => {
// How can I access the `headerRight` component I have set above from here?
...
}
My question is how can I access the headerRight inside my LandingScreen.js? I know I can update or reset the headerRight by:
navigation.setOptions({headerRight:() => <NewHeaderRightComponent/>})
But now what I need is to access the previous already set component, not setting a new one. How to do that?
Edits to the answer as per the request received in comments. The answer is the same. This is just further demonstration on how to use it.
// The screen component where you want to pass the state.
const Screen = (props) => {
const [color, setColor] = useState("#CCCCCC");
const { navigation } = props //This is important or else UseEffect will be called each time any of the props change
useEffect(() => {
navigation.setParams({ color: color }); // Where its being passed.
}, [color, navigation]);
return (
<>
<Button onPress={() => setColor("#800000")} /> // Change the color state to Maroon
<Button onPress={() => setColor("#FED700")} /> // Change the color state to Gold
</>
)
}
Your Header Component:
const MyHeaderComponent = (props) {
return(
<View style={{ backgroundColor: props.bgColor }} />
)
}
Then you can retrieve this bit in headerRight. Like this:
headerRight:() => <MyHeaderComponent bgColor={route.params.color} />
Note: This method is valid for React Navigation v5. Version 4 has a getParams() function to retrieve the params, but it was dropped in Version 5.
Original Answer
You can create a useState hook in the screen and pass its value into your header component. So, when the header component updates the state, it can be accessed from within the screen where you have defined the state.
you can use setParams() function to set the params you want to use in the header component. Then, use route.params.nameofyourprop to get them in the headerComponent, where you can consume it.
This is to pass params from outside the header to inside of it.
headerRight:() => <MyHeaderRightComponent propname={route.params.propvalue} />
This to to set the Params from outside your header which you can access inside the headerRight component.
const [values, setValue] = useState()
navigation.setParams({propname: value})
This way you can pass state between the header and the screen.
You can also pass the setValue function of the useState in this manner, but it will throw a warning because functions are objects in Javascript and thus its not possible to index them... or something on those lines.

Invariant Violation: Maximum update depth exceeded using react Navigation v5

I have a login screen which I have placed in stack. After user logs in successfully he is redirected to home screen which is a drawer screen. One of the options of drawer screen is logout, so on click of it user should be logged out. Following is my code for logout screen. I am just showing a progress bar in logout screen in ui but in useEffect hook, I am calling the following code
navigation.reset({
routes: [{name: LOGIN_SCREEN}],
});
I also tried calling the above method in useLayoutEffect but then the logout button just hangs.
My Navigation stack looks something as follows
<Stack.Navigator>
<Stack.Screen
name={LOGIN_SCREEN}
component={LoginScreen}
options={{headerShown: false}}
/>
<Stack.Screen
name={HOME_STACK_SCREEN}
component={DrawerStack}
options={{headerShown: false}}
/>
// ...
</Stack.Navigator>
My drawer component as follows
<Drawer.Navigator
drawerStyle={{backgroundColor: BLUE_COLOR_1}}
drawerContentOptions={{labelStyle: {color: '#FFF'}}}
>
<Drawer.Screen
name={HOME_SCREEN}
component={Home}
options={{
// ...
}}
/>
<Drawer.Screen
name={LOGOUT_SCREEN}
component={Logout}
options={{
// ...
}}
/>
// ...
</Drawer.Navigator>
Following is my logout component
const Logout = ({navigation}) => {
async function logout() {
try {
await AsyncStorage.clear();
navigation.reset({
index: 0,
routes: [{name: LOGIN_SCREEN}],
});
} catch (e) {
Alert.alert(e.toString());
console.log(e.toString());
}
}
useEffect(() => {
logout();
}, []);
return <ProgressBar />;
};
Invariant Violation: Maximum update depth exceeded using react Navigation v5 occurs when something going in an infinite loop
navigation.reset({
routes: [{name: LOGIN_SCREEN}],
});
When you say you put the above code in useEffect, I suspect that hook is getting called multiple times.
I saw you code, to be honest it does't look good to me if we think of auth flow, Here are reason why I am saying this. Do check this link step by step , you get to know how simply you can achieve this auth flow in proper manner. React Navigation Auth Flow, this problem comes when we try to navigate screen from android back button , user can go back to login after you navigate user to home page on success login
Second you can try one condition in you current code.
You need to check one condition before calling the logout function in you screen
useEffect(()=>{
if(check if user value if still there in async storage ) {
**Here you can excute you code for log out **
}
}
And instead of using reset action from navigation use popToTop , Here you can find the ref how you can us PopToTop or use this
navigation.popToTop()
Cheers !!