I am struggeling with passing parameters between stacks / screens.
I have the following app container with several stacks:
export default createAppContainer(createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
Auth: AuthStack,
Tab: TabStack,
Menu: MenuStack
},
{
initialRouteName: 'AuthLoading',
}
));
In the Auth Stack I have the Login Screen. After login I would like to pass the userName to the other stacks, e.g. in order to establish a web socket connection to my XMPP server.
this.props.navigation.navigate('Tab', { userName: loginData.username })
Unfortunately, I don't have any idea how to access the userName parameter. I will always get undefined when calling navigation.getParam('userName') (I can confirm that loginData.username is not undefined).
Is it correct that parameters are not passed between stacks?
If yes then I know at least the reason for this behavior. But how can I pass the userName then (I read that AsyncStorage is a kind of an anti-pattern for these kind of information and setting up Redux for this case seems a bit over-engineered) ?
In the route you've gone to, you can access your parameters like this:
this.props.navigation.state.params.userName
This is located under "Summary" at the bottom of the documentation page. It's always worth checking if params is not null, since if there's no params this.props.navigation.state.params returns null.
if(this.props.navigation.state.params && this.props.navigation.state.params.userName) {
//do something
}
This was the original way of getting route params before the navigate.getParam() function was introduced in 2.X
I would highly recommend you checkout this package:
https://github.com/vonovak/react-navigation-props-mapper
Simplifies this flow a lot.
I could fix my issue by navigating to a concrete route instead of the stack name.
this.props.navigation.navigate('Map', { userName: loginData.username });
So Map route is part of the TabStack
It seems that it NOT possible to pass parameters to a "stack". It seems that parameters can only be passed to a "screen."
Passing parameters to a nested screen (a "screen", which is part of a "stack") has a different approach than passing parameters to a "screen", which is defined as a "tab".
// example of a stack definition
const Stack_1 = () => {
const MyStack = createStackNavigator();
return (
<MyStack.Navigator>
<MyStack.Screen name="Screen_1_in_Stack_1" component={Screen_1_in_Stack_1} />
</MyStack.Navigator>
);
};
// example for passing parameters to a screen, which is part of a stack:
navigation.navigate('Stack_1', {
screen: 'Screen_1_in_Stack_1',
params: { userID: '123' },
});
Related
I'm actually doing a login system and when my user is logged, the login component has to send data to my main screen but I don't really understand how to do it.
Actually I've this when user is logged :
User.username = data.username;
User.id = responseJson.id;
User.token = responseJson.token;
this.props.navigation.navigate('Main', { User: User });
User is where everything is saved and it works.
Data is sended to Main a Switch Navigates inside an AppContainer :
export default createAppContainer(createSwitchNavigator(
{
Auth: { screen: AuthStack, navigationOptions: { tabBarVisible: false } },
Main: { screen: MainStack },
},
{
initialRouteName: "Auth"
}),
);
so it goes on Mainstack who's a bottomTabNavigator and works like that :
const MainStack = createBottomTabNavigator(
{
Services: {
screen: Home,
navigationOptions: {
tabBarLabel:"Services",
tabBarIcon: ({ tintColor }) => (
<Icon name="home" size={30} color="#0033CC" />
)
},
},
I know it's not complete I've other screens not only Services, just wanted to avoid too long function on paste.
So is that possible to send this data "User" to my Home screen ?
I'm open to any suggestion or answer, that's my first react Native project so there is maybe some mistakes :)
You'll probably want to persist the user data in the app (save it in the storage for when the user leaves the app then enters he stays logged in). That's why react-navigation has the switchNavigator (the one you are using). In order to persist data, you can use the AsyncStorage to save the user data and in the switch navigator constructor check whether the user data is already available in the storage or not then decide whether to navigate to the app or the authentication page. In fact React Navigation provides an example on this: https://reactnavigation.org/docs/en/auth-flow.html . Since you're new to RN, here's an extra tip on managing data: you can use a State in every component that is only for that specific component (which is also reactive), you can use global data (store) shared between all the components in the app (check react redux/flux/mobx), you can share data from parent components to children components by passing data/functions in the props (aka attributes), and you can use the new React context api to use a provider (also like a store shared between parent and children components)
I've made something maybe a bit more understandable :
I get my user data in component login from an external api and it works well. To go from login to home I'm using this :
this.props.navigation.navigate('Main', { User: User})
So it goes on Main who opens Mainstack Screen who's a bottomTabNavigator and if I don't do any mistakes my User data that I've sent is on this Mainstack but I don't know how to get it and how to share it to the other components.
Every component got his own js file, there is only the App container and Authstack/Mainstack that are on the same file.
Hope you understood it !
I have a switch navigator:
export default createSwitchNavigator({
AuthLoading: AuthLoadingScreen,
EditProfile: EditProfileScreen,
Login: LoginScreen,
Register: RegisterScreen,
ForgotPassword: ForgotPasswordScreen,
Main: MainTabNavigator,
MainLoading: MainLoadingScreen,
TripReport: TripReportScreen,
},
{
initialRouteName: 'AuthLoading',
});
When I am on 'Main', a tab navigator, I have a button that navigates me to the 'TripReport' screen:
onPress={() => navigation.navigate('TripReport', {tripReport: tripReport})}
When on the TripReportScreen, I added an event listener for the Android back button to return to the 'Main', but also to the specific Tab:
componentDidMount() {
// Add event listener for the Android back button.
BackHandler.addEventListener("hardwareBackPress", () => {
this.props.navigation.goBack(null);
return true
})
}
The goBack function isn't working. The only way I've gotten it to work is
navigation.navigate('Main)
but this reloads 'Main' instead of going back to the state the screen had.
Yup, This is correct behaviour.
If I had to quote reactnavigation
Optionally provide a key, which specifies the route to go back from. By default, goBack will close the route that it is called from. If the goal is to go back anywhere, without specifying what is getting closed, call .goBack(null); Note that the null parameter is useful in the case of nested StackNavigators to go back on a parent navigator when the child navigator already has only one item in the stack.
This would work only with StackNavigators.
Hope this helps.
Thats because when you call this.props.navigation.goBack on Main, this.props.navigation actually refers to your MainTabNavigator. Thats why your back action did not work.
You need to reach the correct SwitchNavigator in order to dispatch a back action to your SwitchNavigator, check out a few ways suggested on this thread.
Or, you can also create a reference to your switchNavigator, checkout this answer here
https://stackoverflow.com/a/54878858/2963461
I am using React navigation I am looking for a way to pass parameters into my navigation stack as follows. I have actually one screen which I want to use X amount of times. It only needs an url and a title, and based on the url it should do exactly the same for each url.
So i want to create an object like so:
const urls = {
{title: 'foo', url: 'https://someurl'},
{title: 'bar', url: 'https://someotherurl'}
}
And now in my Navigation component I would like to do something like:
export default createMaterialTopTabNavigator({
SomeKey: {
// Loop here over the urls and create a component and pass props.
}
});
My issue is that I can't find in the documentation how to pass the title and url parameter via the navigator to the specific screens.
Any Suggestions?
I can help you with one part, you can pass variables from Navigator like this
export default createMaterialTopTabNavigator({
ScreenOne: {
screen:props=> <ScreenOne {...props} screenProps={yourpropsr}/>
}
});
The documentation has example for StackNavigator but I hope this will work for TabNavigator too. Documentation link here
I'm doing something similar in my app, but I'm grabbing my array from an API and build my navigation upon that. For each item in my array, I build the same screen setup in a tab navigation and have them all available from a drawer navigator.
You could do something like this:
let NavigatorConfig = {};
urls.forEach(item => {
NavigatorConfig = {
...NavigatorConfig,
[item.title]: {
screen: props => <MyComponent {...props} url={item.url} />
}
};
});
export default createMaterialTopTabNavigator(NavigatorConfig);
I am using redux and react native in my mobile application.
I want to redirect to another screen based on API response. I don't know how to do that.
I have used action, reducer and store in my application.
Can you please help me to find standard solution?
Thank you in advance.
One approach to handle user navigation, for instance registering a user is to first define three actions : register, registerSuccess, registerError.
The one responsible for handling navigation will be registerSuccess that will only return a type 'REGISTER_SUCCESS'.
Then you're reducer will look like this (i'm using immutable.js https://facebook.github.io/immutable-js/docs/#/)
const initialState = fromJS({
submitSuccess: false,
});
function reducer(state = initialState, action) {
case REGISTER_SUCCESS:
return state.update('submitSuccess', (v) => !v));
default:
return state;
Then in your container you can use componentWillReceiveProps to redirect your user
componentWillReceiveProps(nextProps) {
if (nextProps.submitSuccess) {
// redirect your user
}
}
See doc how to connect navigation to Redux -> https://reactnavigation.org/docs/guides/redux
I'm using React Navigation library for my React Native project and struggling to understand how to handle state with it.
In normal React Native application I can have state at the top level component and pop events from child components via props, however with React Navigation it seems that I cannot pass any props to components used as Screens.
After reading through related GitHub issue it seems that library devs are very opinionated in forcing everyone to use some kind of global event handler - redux or mobx, I guess.
The handler which needs to modify the following state. I got stuck when I started to try to move the state inside the app as I couldn't figure out how to:
Pass the handler to the TaskForm component.
Pass the state as props to TaskList if its rendered as part of App.js
Please, avoid replying "just use redux". I believe that using redux in this example would be massive overkill.
I use react native and react navigation in my app without redux, and so far it’s working great. The trick is passing screenProps all the way down the line.
For example, I have a More view. I create a basic More view with 2 sub views in a stack:
class More extends Component {
render() {
return <something>
}
}
class SubView1 extends Component {...}
class SubView2 extends Component {...}
Then I create the stack:
const MoreStack = StackNavigator({
More: {
screen: More
},
SubView1: {
screen: SubView1,
},
...
}, options);
And then I create a wrapper class that I export:
export default class MoreExport extends Component {
static navigationOptions = {
title: "More"
}
render() {
return <MoreStack screenProps={this.props.screenProps} />;
}
}
If all of this is in More.js, I can just import More from More.js and use it anywhere else.
If I then pass in screenProps to my root view and then have a wrapper class for each layer, I can pass the screenProps all the way down, and all views can access them using this.props.screenProps.
I use a wrapper like the one above around each StackNavigator and TabNavigator, and the screenProps are passed all the way down.
For example, in my root class’s render method, I could do:
return <More screenProps={{prop1: something, prop2: somethingElse}} />
And then the More class and each SubView in the MoreStack would all have access to these props.
Feel free to let me know if you want more clarification!
Disclaimer: I don’t know if this is the correct or recommended way to do it, but it does work
You can set param to navigation like this:
static navigationOptions = ({ navigation }) => {
return {
tabBarIcon: ({ tintColor, focused }) =>
<View>
<Icon name="bell-ring" size={24} color={focused ? 'green' : 'black'} />
{(navigation.state.params.badgeCount && navigation.state.params.badgeCount > 0) ?
<Text>{navigation.state.params.badgeCount}</Text>
:
<View></View>
}
</View>
}
}
and change badgeCount by:
this.props.navigation.setParams({ badgeCount: 3 })
After being inspired by steffeydev I looked more around react community issues and found a very simple example of wrapper using function.
It's surprisingly simple solution and I don't know why I didn't think about it before.
The function is the following:
const createComponent = (instance, props) =>
navProps => React.createElement(instance, Object.assign({}, props, navProps));
Thanks for inspiration and pointing me to screenProps which lead me to finding this solution.
I was struggling with the same issue and tried some of the other answers before discovering the following part of the documentation for React Navigation: https://reactnavigation.org/docs/en/stack-navigator.html#params.
Essentially, each Screen in the Stack can be passed params which can include handlers and then the various screens can interact with the application state.
My general structure is then to have an App class with state and handlers and the handlers are then passed into each Navigation Screen as needed. I'm not sure if I have this pattern right, but it's the way I understood the general React tutorial.
Example: In my demo app, I have a page flow like this:
Park finder screen -> Park detail screen (with Bookmark action)
Bookmark list screen -> Park detail screen
If you find a park, you can click on a Bookmark button which adds the park to the list of bookmarks shown on the Bookmark screen. You can then click on a park bookmark to see the details.
My App looks generally like this:
import React, { Component } from 'react';
// Screens
import ParkFinderScreen from './Components/ParkFinderScreen';
import ParkBookmarksScreen from './Components/ParkBookmarksScreen';
import ParkDetailsScreen from './Components/ParkDetailsScreen';
// Navigation
import { createStackNavigator, createBottomTabNavigator, createAppContainer } from 'react-navigation';
class App extends Component {
constructor(props) {
super(props);
this.state = {
bookmarks: new Map()
};
}
// Bookmark the park shown in the detail section.
handleBookmark (park) {
let newBookmarks = this.state.bookmarks;
newBookmarks.set(park.parkCode, park);
this.setState({
bookmarks: newBookmarks
});
}
render() {
const FinderStack = createStackNavigator(
{
ParkFinder: {
screen: ParkFinderScreen
},
ParkFinderDetails: {
screen: ParkDetailsScreen,
params: {
handleBookmark: (park) => this.handleBookmark(park),
}
},
}
);
const BookmarksStack = createStackNavigator(
{
ParkBookmarks: {
screen: ParkBookmarksScreen,
params: {
bookmarks: this.state.bookmarks
}
},
ParkBookmarksDetails: {
screen: ParkDetailsScreen,
},
}
);
const AppNavigator = createBottomTabNavigator(
{
Bookmarks: BookmarksStack,
Finder: FinderStack,
}
);
const AppContainer = createAppContainer(AppNavigator);
return (
<AppContainer/>
);
}
}
export default App;
I'm using Apollo Client, but I've removed those parts.
In the Screen components, you can access the props like other ones using this.props.navigation.getParam('bookmarks').
One issue I encountered was that whenever I change the App state, I'm taken to the first screen. The state is updated, but it's a little disorienting. I'm not sure if there is a way to update the App state while staying on the screen. I can sort of understand that given the App state has updated, all the children need to be updated and so the current screen (which is part of a child I think) is reset. I don't know if that is a limitation of the system or a byproduct of how I designed the components.
I hope this helps someone. This seems to keep with the intended behavior of React Native. The library team really seems to want you to not use Redux. https://reactnavigation.org/docs/en/redux-integration.html