In the docs from DrawerNavigatorItems it have the following property:
items - the array of routes, can be modified or overridden
It says you can modify the array.
How can I do that?
It doesn't explain what is an "array of routes". It's just strings? An object?
An example of what I want to do is get the full array and add an extra one at the end, something like
items: [...items, someNewItem]
One solution is to define our customized Component. Furthermore, we could utilize Redux to manage the state.
Excerpt from an APP which I created:
//The navigation part
const MainDrawer=createDrawerNavigator({
MainNavigation,
K:KChartViewScreen
},{
contentComponent:CoinDrawer,
edgeWidth:-1 //to disable gesture from bound open the drawer in unexpected screen. but you could do some work to enable it and without problem, I just handle in an easy way.
});
The following is the CoinDrawer:
class CoinDrawer extends React.Component{
renderMarkets(){
...
}
render(){
return (
<View>
<ScrollView>
<SafeAreaView style={styles.container} forceInset={{ top: 'always', horizontal: 'never' }}>
{this.renderMarkets()}
</SafeAreaView>
</ScrollView>
</View>
)
}
function mapStateToProps(state){
return {
config:state.config,
crypto:state.crypto,
trade:state.trade
}
}
export default connect(mapStateToProps,{SetCurrentMarket})(CoinDrawer);
Hope this can help you.
You can pass your drawer navigation items in your navigation file. You can create one like below
import { createDrawerNavigator } from 'react-navigation-drawer'
const MainNavigator = createDrawerNavigator(
{
Main,
Landing,
},
{
contentComponent: Drawer,
},
)
I hope you understand we can make different types of navigators like switch, stack, drawer, etc.
After this, you can call your drawer-navigator inside AppNavigator like below
const AppNavigator = createStackNavigator(
{
MainNavigator,
Account,
Success,
},
{
initialRouteName: 'MainNavigator',
headerMode: 'none',
},
)
Now whatever route you will insert in MainNavigator object, it would be shown into the drawer.
There may be several ways of using navigation service, I used this one.
Hope this helps.
Related
I want to pass as a parameter a username to my drawernavigator, so that i can use it as the drawerlabel text.
Can anyone shine a light on this issue?
PS: I apologize for my poorly written question, i'm new to coding!
const RootDrawerNavigator = createDrawerNavigator({
Perfil:{
screen: PerfilStack,
navigationOptions: {
drawerLabel:(
//this is where i want the username
),
drawerIcon: (
<Image source={require('../../assets/user.png')} style={{height:50, width:50, marginLeft:'50%', marginRight:'5%'}}/>
),
},
},
export default createAppContainer(RootDrawerNavigator);
I'm calling this component here, on another file:
export default function Main() {
return (
<Navigator/>
);
}
I have a problem to navigate away from a drawer navigator's content component
Nav's (navigator root screen component) render()
render() {
const Drawer = createDrawerNavigator({
Home : {screen : Home},
Settings : {screen : Settings}
},{
contentComponent: DrawerContent,
drawerWidth: 200,
drawerPosition: 'left',
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
});
return (<Drawer/>);
}
DrawerContent's render()
render() {return (<FlatList
data : ['Home', 'Settings', 'Logout'],
renderItem : {({item, index}) => {
return (<Button
title = {item}
onPress = {() => {
if(index != 2) this.props.navigation.dispatch(NavigationActions.navigate({routeName: item}));
else this.pleaseTakeMeOutOfHere();
}}
/>);
}}
/>)}
Suppose I want to get out from Nav to an external screen component called Login, how I should define pleaseTakeMeOutOfHere ?
You would probably need to use a SwitchNavigator for this.
You can define two stacks for this LoginStack, HomeStack.
export default createSwitchNavigator(
{
LoginStack: LoginStack,
HomeStack: HomeStack,
},
{
initialRouteName: 'HomeStack',
}
);
and then use this.props.navigation.navigate('HomeStack'); to navigate out of LoginStack
Switch navigator does not handle back actions and it resets routes to their default state when you switch away.
If I export to the parent navigator (like StackNavigator or SwitchNavigator), the component which render the DrawerNavigator as a child like I did in the question, calling this.props.navigation.navigate('Login'); from contentComponent not working at all. I don't know why. I guess because the navigation chain is broken by doing such. I can't even remember why I did that. I've finally fixed it by export the DrawerNavigator directly
const Drawer = createDrawerNavigator({...},{...});
export default Drawer;
And to exit the DrawerNavigator instead of adding it to the stack, I have to replace StackNavigator with SwitchNavigator. Thanks to Pritish Vaidya for the answer
Though this question is specific to my case, I hope anyone will get an idea from here when they face the same issue. Thank you
I'm trying to use "this.props.navigation" in my main react native App but it is undefined. In my others components included with TabNavigator and ScreenNavigator it work.
My App.js
const MyTabView = TabNavigator(
{
Projects: { screen: Projects },
Explorer: { screen: Explorer },
Profil: { screen: Profil }, ...
}
);
const StackView = StackNavigator(
{
global: {
screen: MyTabView,
headerMode: "none",
header: null,
navigationOptions: {
header: null
}
},
TermsOfUse: { screen: TermsOfUse },
LegalNotice: { screen: LegalNotice },
Options: { screen: Options }, ...
}
);
export default class App extends React.PureComponent {
constructor (props) {
super(props);
console.log(this.props.navigation); // <---------- UNDEFINED
}
render() {
return (
<Provider store={store}>
<PersistGate
loading={<ActivityIndicator />}
persistor={persistor}>
<View style={styles.fullScreen}>
<StackView />
<Tutorial navigation={this.props.navigation} /> // <---------- this.props.navigation is UNDEFINED
</View>
</PersistGate>
</Provider>
);
}
}
navigation prop is only available on components that are in navigation stack or child components that are using withNavigation HOC.
Your App class is not included in the Navigator stack, due to which, the navigator class cannot provide any navigation props to it, that is why you are getting an undefined error because the navigation object doesn't exist.
Also if you need to open <Tutorial/> as a first screen, include it as the first element in the StackNavigator. That ways, it'll be always called first.
PS: You can additionally provide navigation={this.props.navigation} element to different components, which does not have to be included in the navigator class. For example, you need to open a dropdown element, which provides a sign out option that navigates to a home screen. In this use case, it'll be better to provide the navigation props as you have provided it in the question. But care needs to be taken, that the class providing the prop should have the object present
EDIT
According to your requirement, you need to display a tutorial for each screen that you open. You can do it via two approaches.
Your way - You can declare it in the stack navigator. But your <Tutorial/> component will contain multiple conditions to check for the route and display the appropriate data ( that's what I can think of right now ).
You can declare a common component that only shows the message in a modal. You can then declare an optional prop for each component, and in each of your components, you can check if this optional prop exists, you can show your common display <Tutorial/> component.
This is what I can think of currently. Will update if I have more thoughts :)
Hope this answers.
I have a TabNavigator, and in each tab is a StackNavigator. Inside the StackNavigator, I have screens. The screens in each Tab do not call each other directly; the TabNavigator handles the screen changes when a tab is pressed.
In the first tab, if the user clicks a button, some data is created. If the user then navigates to the second Tab, I would like to pass this data to the screen in the second Tab.
Here is a demo of the code:
import React from 'react';
import { Button, Text, View } from 'react-native';
import {
createBottomTabNavigator,
createStackNavigator,
} from 'react-navigation';
class HomeScreen extends React.Component {
doIt = () => {
this.props.navigation.setParams({results: ['one', 'two']}); // <--- set data when user clicks button.
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{/* other code from before here */}
<Button
title="Set Results"
onPress={this.doIt}
/>
</View>
);
}
}
class SettingsScreen extends React.Component {
render() {
console.log(this.props.navigation); // <--- console out when user clicks on this tab
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings</Text>
</View>
);
}
}
const HomeStack = createStackNavigator({
Home: HomeScreen,
});
const SettingsStack = createStackNavigator({
Settings: SettingsScreen,
});
export default createBottomTabNavigator(
{
Home: HomeStack,
Settings: SettingsStack,
},
{
}
);
The this.props.navigation.state.params never gets the data results in the second Tab. There isn't even a key for it, so if I try to access this.props.navigation.state.params.results, it will be undefined.
This is confusing because I thought props.navigation is passed to all screens automatically.
How can I pass data from one screen to another through the TabNavigator, using just react-navigation? I have seen answers that say to use Redux, but I would not like to import another library if all I want is to keep some state across screens in different react navigators.
It may seem that this.props.navigation.state.params is only able to old one parameter? Possibly? Try this:
doIt = () => {
this.props.navigation.setParams({results: 'one'}); // <--- set data when user clicks button.
}
console.log(this.props.navigation.state.params.results);
Setting props did not work when passing data across different tabs. I even tried playing with AsyncStorage, trying to save and retrieve them in different tabs.
I ended up using Redux to save my states, and that has worked well so far.
I came across a similar problem. I had a multi page form that the client insisted on having each step be enclosed in a tab on a tab bar. I used the react navigation createMaterialTopTabNavigator to create the navigator and couldn't find an easy way to pass the form data between tabs.
What I end up doing was using react's Context API and wrapped the tab navigator in a root form container that provides the context value to the navigator and routes inside. Here is how I did it:
Root form container
// MultiScreenForm.js
imports...
import MultiScreenFormNavigator from './MultiScreenFormNavigator'
export const FormContext = React.createContext()
class MultiScreenForm extends Component {
constructor(props) {
super(props)
this.state = {
// formDataHere
formUpdaters: {
onToggleOptIn: this.handleToggleOptIn // example updater method
// other
}
}
}
handleToggleOptIn = () => {
// toggle opt in form data with this.setState
}
render() {
return (
<FormContext.Provider value={this.state}>
<MultiScreenFormNavigator />
</FormContext.Provider>
)
}
}
export default MultiScreenForm
Example form page
// ProfileForm.js
imports...
import { FormContext } from './MultiScreenForm'
class ProfileForm extends Component {
render() {
// FormContext.Consumer uses function as child pattern
return (
<FormContext.Consumer>
{ (context) => (
// our form can now use anything that we pass through the context
// earlier, we passed the root form's state including an updater
<button onPress={context.formUpdaters.onToggleOptIn} />
// ...
)
}
</FormContext.Consumer>
)
}
}
export default ProfileForm
Tab navigator
// MultiScreenFormNavigator.js
imports...
import ProfileForm from './ProfileForm'
import { createMaterialTopTabNavigator } from 'react-navigation'
const MultiScreenFormNavigator = createMaterialTopTabNavigator(
{
Profile: ProfileForm,
// AnotherForm: AnotherForm
},
// { navigator options here... }
)
export default MultiScreenFormNavigator
We then render the MultiScreenForm instead of the tab navigator directly.
This worked for me but I feel there should be an easier way to do this. I hope people who read this can share their approaches.
#tempomax
tried same with AsyncStorage but data came in with a delay.
Sometimes you don't need Redux if your app stays small.
So tried to find a way without Redux.
Here is what I came up with
I hope it's not too late to answer.
Solved it with NavigationEvents and setting params to Route.
The problem with tab is that you canĀ“t pass params to screen because navigation.navigate will be triggered automatically if createMaterialTopTabNavigator is swiped or clicked on non-active TabBar Button.
This can be solved with NavigationEvent like follow.
import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
const MyScreen = () => (
<View>
<NavigationEvents
onWillFocus={payload => console.log('will focus',payload)}
onDidFocus={payload => console.log('did focus',payload)}
onWillBlur={payload =>
/*
if screen is about to change this will be triggred
In screen 'MyScreen2' you can get it with navigation.params
*/
this.props.navigation.navigate('MyScreen2', { name: 'Brent' })
}
onDidBlur={payload => console.log('did blur',payload)}
/>
{/*
Your view code
*/}
</View>
);
export default MyScreen;
Now you can get the data in MyScreen2
/* 2. Get the param, provide a fallback value if not available */
const { navigation } = this.props;
const itemId = navigation.getParam('name', 'DefaultName');
const otherParam = navigation.getParam('otherParam', 'some default value');
If you are using React Native Navigation Version 5.x with a DrawerNavigation, you can do this using
in screen 1:
<Button
onPress={() => {
this.props.navigation.navigate(<ScreenNameOfDrawerScreen>,
{screen:'<ScreenNameInTabDrawer>',params:{your_json_Data}});
}} />
in screen 2:
............
render() {
if(this.props.route.params!=undefined){
if(this.props.route.params.your_json_Data!=null){
// Use this.props.route.params.your_json_Data. It is your json data.
}
}
return (
..............
I'm trying to create a menu button in the header area of one of my screens.
However, I can't figure out how to get the onPress navigation to work.
Obviously, the code below will produce an error because navigate is defined after it's first used.
But, when I try to move the const { navigate } declaration up before I set the navigationOptions property, I get an "unexpected token" error in my IDE (visual studio code).
Is there any way around this?
Thanks!
export default class HomeScreen extends React.Component
{
constructor(props) {
super(props);
}
static navigationOptions = {
title: 'Seekers',
headerRight:
<TouchableHighlight onPress={ () => navigate('Menu', {
menu: options
})}>
<Image source={require('./images/menu.png')} style={{width: 50, height: 50}} />
</TouchableHighlight>,
};
render() {
const { navigate } = this.props.navigation;
return (
<View>
... more view stuff...
Customizing the header changed in the last few updates of React Native and passing navigation options changed too. According to the doc Screen Navigation Options, Dynamic configuration is possible and the 'navigationOptions' now can be used as a function which takes navigation Options as parameters.
For example, I wanted a button to close and open my DrawerNavigator in the header. In my router.jsfile, I wrote:
export const Root = StackNavigator({
App: {
screen: AppDrawer,
navigationOptions: ({navigation}) => ({
headerLeft: <BurgerMenu nav = {navigation} />,
})
}
}, {
headerMode: 'screen',
});
In your case, I think you should declare your navigationOptions as a function:
static navigationOptions: ({navigation}) => ({
title: 'Seekers',
headerRight: <TouchableHighlight onPress={ () =>
navigation.navigate('Menu', { menu: options })}>
<Image source={require('./images/menu.png')} style={{width: 50, height: 50}} />
</TouchableHighlight>,
};
I didn't personnally try to set navigationOptions from the component itself. Instead I declare the options in my StackNavigator.
Let me know if it works !
EDIT
How does my navigation work ?
For the navigation side of my application I'm simply using react-navigation.
It would be pretty long to copy all the content of my files here so I created a Gist for you to see how I implemented that.
First, I create a router.js file, in which I define my screens and the type of navigation (here). Focus on the variable named Root, this is my main StackNavigator. This is the entry point of my app.
Second, I edited the index.android.js and index.ios.js so they both render the same component.
import { AppRegistry } from 'react-native';
import App from './src/index';
AppRegistry.registerComponent('obexto_expensemanager', () => App);
Third. What does my App component contains ? This component is the one I use to render my entry point defined in the router.js. What is important in this file is:
import { Root } from './config/router';
class App extends Component {
render() {
return (
<Root />
)
}
}
I use the StackNavigator defined in router.js as my main component for my app.
Why ? I like the way this navigation works because you separate clearly your stacks from your components. And all your navigation is set in a single file. So, in your project you'll have your scene folder with all your components and a router where you connect them.
In my example I wanted to add a button in my header so I could open my DrawerNavigator. The header can be accessed from the navigationOptions when you define your Navigator, that's why I defined the option headerLeft in my App StackNavigator, so it's visible in every screen.
I don't know if this is clear for you but don't hesitate to tell me if it's not and what is still obscur to you. I'll do my best to help !
Just a thought, how about declaring the navigationOptions directly on the StackNavigator (assuming that's what you are using). For example
`
const Nav = StackNavigator({yourRoutes}, {
{
navigationOptions: {...}
}
});
`