Navigate to sub screen from everywhere - react-native

I'm facing an issue. I want to find a single way to go a to specific sub screen from everywhere (from every stack). I need this to handle navigation on notification selection.
So the doc says, if i want to reach a sub screen, i have to proceed like this:
const navigateAction = NavigationActions.navigate({
routeName: 'Profile',
params: {},
action: NavigationActions.navigate({ routeName: 'SubProfileRoute'
}),
});
this.props.navigation.dispatch(navigateAction) // (1)
This works for me only if i'm outside the "Profile" Stack. But if i'm in the profile stack the navigation stucks at the initial screen on the Profile Stack.
So I have 2 questions :
- can we use the method (1) to move throughout the same stack ?
- Is there a way to know which stack the user is currently in ?
this.props.navigation.state doesn't help (because I want the info in a listener located at a specific screen)
Many thanks

Instead of answering your questions, I am going to suggest you an easy alternative. It is to use react-native-router-flux. It is very simple to use and addresses your questions above with very simple codes. After installing the module, add the following to you App.js file:
import { Router, Scene, Actions } from 'react-native-router-flux';
import Home from 'homeLocation';
import AnotherPage from 'anotherPageLocation';
const onBackPress = () => {
if (Actions.state.index === 0) {
return false
}
Actions.pop()
return true
};
const Routes = () => (
<Router navigationBarStyle={{// your style}} backAndroidHandler={onBackPress}
titleStyle = {{// your style}} navBarButtonColor = {"lightgrey"}>
<Scene key = "root">
<Scene key = "Home" component = {Home} title = "homePageTitle" initial = {true}/>
<Scene key = "AnotherPage" component = {AnotherPage} title = "pageTitle" />
// ...other scenes
</Scene>
</Router>
)
export default Routes;
Then, if you want to go to home page from any page, just write Actions.Home() or if you want to go to 'AnotherPage' just write Actions.AnotherPage(). If you want to get in what page that you are currently in, get it from const currentpage = Actions.currentScene();.It return the name of the current scene. For more info, refer this page.

Related

Type error in getting route params within nested navigator using Typescript [react-navigation v6]

Having a navigation type definition as bellow, when I navigate from e.g AOne to BTwo with id:99 the console log of props.route.params shows correct info. But props.route.params.id throws type error
TypeError: undefined is not an object (evaluating 'props.route.params.id')
// navigation related imports in all components
import {BottomTabScreenProps} from '#react-navigation/bottom-tabs';
import {CompositeScreenProps, NavigatorScreenParams} from '#react-navigation/core';
import {StackScreenProps} from '#react-navigation/stack';
// type defenitions
export type StackOneParams = {
AOne:undefined,
BOne: undefined,
// some other screens
};
export type StackTwoParams = {
ATwo: undefined;
BTwo:{id:number};
// some other screens
};
export type TabParams = {
StackOne: NavigatorScreenParams<StackOneParams>;
StackTwo: NavigatorScreenParams<StackTwoParams>;
// ... some other stacks each represent a tab
};
export type RootParamList = {
ROne: undefined; // these screens should stand alone and not blong to any tab
RTwo: undefined;
Tabs: NavigatorScreenParams<TabParams>
}
// navigation from AOne to BTwo
props.navigation.navigate('Tabs', {
screen: 'StackTwo',
params: {screen: 'BTwo', params: {id: 99}}
}); // this part give correct auto complete hints in VSCode and no compilation error
// BTwo component (screen)
//--------------------------------
type Props = CompositeScreenProps<
StackScreenProps<RootParamList, 'Tabs'>,
CompositeScreenProps<
BottomTabScreenProps<TabPrams, 'StackTwo'>,
StackScreenProps<StackTwoParams, 'BTwo'>
>
>;// using CompositeScreenProps to be able to navigate to screens in another tabs
// otherwise just `type Props=StackScreenProps<StackTwoParams, 'BTwo'>` works fine but cannot navigate to other tabs
const BTwo:React.FC<Props> = (props) =>{
console.log(props.route.params)// the log shows {id:23}
// but props.route.params.id gives error and also no auto completion hint
return(...)
}
is this the correct way to define screen props for a specific screen, like what I have in BTwo screen? or the sequence of the composition should be different?
most of the examples (and the official documentation) show the most simple composition where the target screen is not even in second level of nesting (in the official doc profile is not really in nested bottom tabs)
How should I solve the type error in this case?
the image shows the VSCode auto-complete suggestions
Solution using CompositeScreenProps
My other explanations were not quite accurate. The way you have defined the CompositeScreenProp is not correct. Here is the correct way to implement this.
type ScreenProps = CompositeScreenProps<
StackScreenProps<StackTwoParams, "BTwo">,
CompositeScreenProps<
BottomTabScreenProps<TabParams, "StackTwo">,
StackScreenProps<RootParamList>
>
>
The first parameter of CompositeScreenProps contains the type of the navigator that owns the screen. In this case BTwo is owned by StackTwo and this determines the primary navigator, which is a Stack.
The above yields to the correct types as well.
const BTwo = (props: ScreenProps) => {
return <></>
}
Solution using separate types for navigation and route
We can type the navigation object and the route object separately as follows.
type NavigationProps = CompositeNavigationProp<
StackNavigationProp<StackTwoParams, "BTwo">,
CompositeNavigationProp<
BottomTabNavigationProp<TabParams, "StackTwo">,
StackNavigationProp<RootParamList>
>
>
type ScreenPropsA = {
navigation: NavigationProps
route: RouteProp<StackTwoParams, "BTwo">
}
Notice the usage of CompositeNavigationProp and RouteProp here.
Then, use it as follows.
const BTwo = ({ route, navigation }: ScreenProps) => {
return <></>
}
Both, route and navigation are now correctly typed.

"Undefined" error when passing params to another screen in React Navigation

I have 2 screens, EditProfile and Profile. I'm trying to pass the edited name and date from EditProfile to Profile. They are both stacked in a ProfileNavigator, Profile is the first screen that appears and from there, user can tap on a edit profile button and navigate to Edit Profile Screen. How can i pass updated Edit Screen name and date params to main profile? Following code does not work and return Cannot read property "date" of undefined:
const EditProfileScreen = () => {
const navigation = useNavigation();
}
return (
..
<TouchableOpacity style={styles.commandButton} onPress= {() => {navigation.navigate("Profile", {params: {date: date, name: name }})} }
activeOpacity={0.5}>
<Text style={styles.panelButtonTitle}>Submit</Text>
</TouchableOpacity>
);
While this is the code for the Profile Screen:
const ProfileScreen = ({route, navigation}) => {
const { date } = route.params;
const { name } = route.params;
return (
..
<Text>{JSON.stringify(date)}</Text>
<Text>{JSON.stringify(name)}</Text>
)
There is nothing wrong with your approach, but you are missing null handling for the initial render of the profile screen.
Think like this, the first time you navigate to the profile screen there wont be a route.params as you are not passing any parameters which ends up in an error.
But when you move to the edit screen and come back with parameters you will have the route.params which you can access without any issues.
So change your code like this
<Text>{JSON.stringify(route.params?.date)}</Text>
<Text>{JSON.stringify(route.params?.name)}</Text>
Or if you have the profile object in the state of the profile screen, update it using a useEffect hook based on the change of route.params.

Passing params in React Navigation 5

I'm trying to pass a few params between a Tab Navigator. Below is the structure of my program. In bold are the routes
App(Tab Navigator): { Main(stack) & Filter(screen) }
Main(Stack Navigator): { Home(screen) & MediaDetails(screen) }
I have a button on the screen associated with Filter which has an onPress() function. I'm passing a few params(but let's only consider the param to for the sake of this question).
this.props.navigation.navigate('Home', {
to: this.state.to,
}
Now in the screen associated with Home, I'm reading the param inside the state like this:
state = {
to: this.props.route.params.to
}
Inside App.js, I've set the initial value of to to be '2020' like this:
<Stack.Screen
name="Home"
component={HomeScreen}
initialParams={{
to: '2020'
}}
/>
The initial value is indeed set to 2020. I press the button. Let's say I'm setting to to 1900. Just before this.props.navigation.navigate executes, I console.log(this.state.to) the value and it is indeed updated to 1900. However, as the screen changes to Home, the value reverts back to 2020(observed via console.log)
Could someone point out the cause for this spooky behavior? I've been trying to debug this for many hours with no luck. React Navigation 5 is pretty new as well so couldn't find anything similar online. Any help will be greatly appreciated. Thank you for reading all the way.
Edit: Issue has been resolved and full code has been removed!
/* 1. Navigate to the Details route with params */
onPress={() => {
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/* 2. Get the param */
const DetailsScreen = ({ route, navigation }) => {
const { itemId } = route.params;
const { otherParam } = route.params;
return (
<View></View>
);
};
for more info: https://reactnavigation.org/docs/params/
I solved this problem by updating the state in shouldComponentUpdate().
This bug was due to the state not being updated as the screen remained mounted.
This was possible because of ravirajn22 on GitHub who explained to me the reasoning behind this bug and a possible solution.
Link to the answer: https://github.com/react-navigation/react-navigation/issues/6863
As it is stated Here, you can do it like this:
navigation.navigate('otherStackName', {
screen: 'destinationScreenName',
params: { user: 'jane' },
});
In Functional Component write simple logic like this:-
In the parent component
<TouchableOpacity onPress={()=>navigation.navigate('childRoute', {data_Pass_to_Child: "Hello I am going to child component"})}>
In the child component
const Chat = ({route}) => {
console.log(route.params.data_Pass_to_Child);
return(whatever based on your UI)
}
Note:
Replace childRoute with your route name and also replace data {data_Pass_to_Child: "Hello I am going to child component"} with whatever data you want to pass.
send
props.navigation.navigate('YourScreenName', {
your_key: any_value
});
receive in YourScreenName.js
var valueReceived = props.navigation.state.params.your_key;

Pass variables via `this.props.navigation` multiple times

So, for begging, in react-native-navigation there's a possibility to pass some data via this.props.navigation.navigate().
Here's how you should pass the data :
this.props.navigation.navigate('RouteName', {/*Data to pass*/})
And so, moving to the problem
The case where this problem was encountered :
I have a list of items which I click on and I navigate to the next screen, the data of the pressed item being sent during the navigation process, and when I get to the next screen, the passed data is assigned to state, and I further operate with it. Here are the commands which I use for passing data:
Pass data
this.props.navigation.navigate('Screen2',{param1: value1, param2: value2})
Receive data
ComponentWillMount = () => {
const param1 = this.props.navigation.getParam('param1');
const param2 = this.props.navigation.getParam('param2');
this.setState({param1, param2)}
}
The Problem itself
My Problem is that if I go back to the first screen, and press on another item, then it's data isn't passed via this.props.navigation.navigate(), the data on the second screen remains unmodified from the first navigation process. How this problem can be resolved?
I think i figured it out,
I was able to replicate the issue using drawerNavigator and tabbed navigator in the react-navigation 3.0.5.
Basically they save the components even when you run navigation.goBack.
The screen isn't being mounted again so it doesnt call componentWillMount() and it doesn't check for data there.
there are 2 (edit 3) ways to fix this.
one is to turn off this performance enhancement
const MyApp = createDrawerNavigator(
{
Screen1: Screen1,
Screen2: Screen2
},
{
unmountInactiveRoutes: true
}
);
The second option and the more elegant one is to subscribe to navigation events
componentWillMount() {
console.log("mounting");
const willFocusSubscription = this.props.navigation.addListener(
"willFocus",
() => {
console.debug("willFocus");
const thing = this.props.navigation.getParam("thing");
const thing2 = this.props.navigation.getParam("thing2");
this.setState({thing, thing2});
}
);
}
Just dont forget to unsubscribe in componentWillUnmount
componentWillUnmount() {
willFocusSubscription.remove();
}
The third way is basically the same as the second but subscribing declaratively. This means no componentWillMount or WillUnmount.
First a callback to set the state appropriately
willFocus = ({action}) => {
console.debug("willFocus", action);
const thing = action.params["thing"];
const thing2 = action.params["thing2"];
this.setState({thing, thing2});
};
now in render add the component
render() {
console.log("data is:", this.state.thing);
return (
<View style={styles.container}>
<NavigationEvents
onWillFocus={this.willFocus}
/>
.... rest of render body
</View>
);
}
This doesn't display anything but it takes care of subscribing and unsubscribing.

go two screen back with single press event using react-navigation in react native app

I am using reactnavigation component from https://reactnavigation.org and using the code below i am going one screen back
<Button
onPress={() => goBack()}
title="Go back from this HomeScreen"
/>
how can i go 2 screen back on single press action
I am using this code to initialize the navigator
const RouteConfigs = {
Login: {screen:Login},
Home: {screen:Home},
Chat: {screen:Chat},
Facebook: {screen:Facebook},
Facebookgallery: {screen:Facebookgallery}
}
const StackNavigatorConfig = {
headerMode: 'none',
}
export default StackNavigator(RouteConfigs, StackNavigatorConfig)
I navigate from home to Facebook with this code :
() => this.props.navigation.navigate('Facebook', {user:this.state.user})
and from Facebook to Facebookgallery with this code :
onPress={ () => this.props.navigation.navigate('Facebookgallery', {user:this.state.user}) }
now i want to go back from Facebookgallery to Home directly with some parameters
I know it's an older question but what I use is:
navigation.pop(n);
Takes you to the previous screen in the stack. If you provide a number, n, it will specify how many screens to take you back within the stack.
https://reactnavigation.org/docs/en/navigation-prop.html
you can use navigation.pop(screenCount)
with screenCount as integer
navigation.pop(2)
here is for refference
https://reactnavigation.org/docs/stack-actions#pop
React Navigation is updated not to use pop function directly, use dispatch function with StackActions.
navigation.dispatch(StackActions.pop(2));
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_A });
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_B });
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_C });
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_D });
Now you are on-screen D and want to go back to screen A (popping D, C, and B). Then you need to supply a key to goBack FROM:
navigation.goBack(SCREEN_KEY_B)
https://reactnavigation.org/docs/en/navigation-prop.html#going-back-from-a-specific-screen-with-goback
The "proper" solution right now is:
Get the key for the screen after the screen you want to go to. In
your case, you'll need to get the key for the Facebook screen.
Call NavigationAction.back(key: 'key-of-previous-screen'), and it
will pop your navigation stack as if you were on the screen with that key
Code example where I go back multiple screens in a thunk (relevant if you use redux):
export const postItemAndReturnToMap = item => (dispatch, getState) => {
const {nav} = getState();
// Get the key for the screen after/above the root view and use that as reference
// to return to the root view. This is hardcoded for my stack setup, as you can see
// there are quite a few nested StackNavigators in my setup
const {key} = nav.routes[0].routes[0].routes[0].routes[1];
// Dispatch whatever action you want
dispatch(postItem(item));
// This will now go back multiple screens, in my case to the
// bottom of the top stackNavigator
return dispatch(NavigationActions.back({key}));
};
This isn't pretty. Let's hope react-navigation implements something like .back(2) to make things easier in the future.
If you are using push then you can go to root screen with this
this.props.navigation.popToTop();
and to Push
this.props.navigation.navigate('SummaryScreen', {
otherParam: 'anything you want here',
});
You could define an additional navigation action type, e.g. POP_TWO_ROUTES and overwrite StackRouter.getStateForAction(passedAction, state) like that (only exemplary):
...
const defaultGetStateForAction = AppNavigator.router.getStateForAction;
AppNavigator.router.getStateForAction = (passedAction, state) => {
if(state && state.routes && state.routes.length > 2
&& passedAction.type === 'POP_TWO_ROUTES') {
let routes = state.routes.slice();
routes.pop();
routes.pop();
return {
index: routes.length - 1,
routes: routes
};
}
// default behaviour for none custom types
return defaultGetStateForAction(passedAction, state);
}
...
Then in your screen component you can do something like that:
...
onPress={() => this.props.navigation.dispatch({
type: 'POP_TWO_ROUTES'
})}
...
See also https://reactnavigation.org/docs/routers/#Custom-Navigation-Actions.
It sounds like you're looking for something along the lines of #reset (https://reactnavigation.org/docs/navigators/navigation-actions#Reset).
I haven't used this library, but you likely are trying to go back to the navigation stack's root screen. Other libraries I've used for nav have some sort of popToRoot or something similar.
Try this
onPress={() => {
props.rootNavigation.dispatch(
NavigationActions.reset({
index: 0,
key:null,
actions: [NavigationActions.navigate({ routeName: 'Home' })]
})
)
}}
This should work:
this.props.navigation.pop(2)
For React Native V6 2022
Simple Go Back "goBack"
navigation.goBack()
Go Back tow screen or 3 on specific the name "navigate"
props.navigation.navigate("RouteName")
Navigate to the top screen "popToTop"
navigation.popToTop()