I want to have a drawer menu in my react-native application. so like you see i'm using stack-navigation of react-native in my code to determinate the routes and a DrawerNavigator which is connected to application in screen field of stack!:
const Application = StackNavigator({
Home: {
screen: Login,
}
,
Profile: {
screen: Profile,
}
});
const easyRNRoute = DrawerNavigator({
Stack: {
screen: Application
}
}, {
contentComponent: DrawerMenu,//<XXXDraw
contentOptions: {
activeTintColor: '#e91e63',
style: {
flex: 1,
paddingTop: 15,
} });
i use Application like </Application> in app.js. my DrawerMenu which is pointed by "//
export default class DrawerMenu extends Component {
render() {
return (
<View>
<Text>Menu</Text>
</View>
);
}
}
and my page -which i want to show menu in that and got problem- contains this codes:
export default class Profile extends React.Component {
static navigationOptions = { header: null };
constructor(props) {
super(props);
this.state = {
active: 'Today',
};
}
navigate() {
this.navigate.navigation.navigate('DrawerOpen'); // open drawer
}
render() {
return (
<View style={styles.countainer}>
<Text style={styles.header}>Profile</Text>
<Button onPress={this.navigate} title="Menu"></Button>
</View>
);
}
}
the problem is this: when i click on the Menu Button. i got this Error:
undefined is not an object (evaluating
'this.props.navigation.navigate')
i tried some codes like :this.props.navigation.navigate('Home'); in component and they are working on but i dont know why it doesn't work on when i take "DrawerOpen" argument in this.navigate.navigation.navigate.
I did some search about that. someones told my remove this.prop in code, but just the error changed to navigation is undefined. i'm stuck in trouble because of this Error. its really lake of obvious tutorial about DrawerMenu in net
Can you use this code:
<Button onPress={() => this.props.navigation.navigate("DrawerOpen")} title="Menu"></Button>
Please read the following:
https://reactjs.org/docs/handling-events.html
A short quote from the website:
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
This is not React-specific behavior; it is a part of how functions work in JavaScript. Generally, if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method.
use easyRNRoute insted of Application in your app.js, may be this will work
Related
I have a custom header for my stack navigation and I want to navigate to another page when I press on an image. But when I press the image I get an error undefined is not an object (evaluating _this.props.navigation.navigate')
In my App.js
const ProfileStackNavi = createStackNavigator({
stackAndTab:{
screen:ProfileTopTabNavigator,
navigationOptions: {
header: (
<MyPageTabBarHeader />
)
},
}
})
In my custom header class
export default class MyPageTabBarHeader extends Component {
constructor(props) {
super(props)
}
render() {
return(
<View style={{width:375,height:250, backgroundColor:'white'}}>
<TouchableOpacity onPress={()=>this.props.navigation.navigate('Register')}>
<Image
style={{width:23, height:23}}
source{require('../Components/Assets/register.png')} />
</TouchableOpacity>
</View>
</View>
)
};
}
I also tried <MyPageTabBarHeader navigation={this.props.navigation}/> but then it gives me the error undefined is not an object (evaluating this.props.navigation.navigate') the same error as previously but without an _ before this.
Edited
In my App.js I have createAppContainer. It looks like this:
const StartSwitchNavigator = createStackNavigator(
{
one:{
screen:screenOne,
},
{
two:{
screen:ProfileStackNavi,
}
);
const App = createAppContainer(StartSwitchNavigator);
export default App;
the navigation isnt being passed properly. try passing it this way :
const ProfileStackNavi = createStackNavigator({
stackAndTab:{
screen:ProfileTopTabNavigator,
navigationOptions: ({navigation}) => ({
header: <MyPageTabBarHeader navigation= {navigation} />,
})
}
})
hope it helps!
It seems the navigation prop isn't being passed down, that's normal for a custom header. The prop is usually only passed to screens/views. You simply have to import withNavigation from 'react-navigation' in your header component and then export default withNavigation(MyPageTabBarHeader) at the bottom of the file instead of when you declare the class.
You will then have access to the navigation prop. Here's some doc about the function.
I'm new to ReactNative and started to using TabBarIOS component for a project. I have TabBarIOS component which has 5 different TabBarIOS.Item Component. These each all point another component to present. These different components are all have different backgroundColor's and styles and titles but when I change the selectedTab the change has happened but the properties of components such as backgroundColor not affect the presented component. For testing, I've log a text in componentWillMount method of the Component class for each one. And they logged successfully. Here is the partial components. For the first Component which is named as Restaurants the title is correctly showing in navigationItem but in others navigationItem's title is empty.
I've called my components as ViewControllers.
class RestaurantsComponent extends Component{
componentWillMount(){
console.log('restauranscomponent will mounted');
}
render(){
return(
<View style={{flex:1, backgroundColor:'blue'}}>
<Text>ASDFSADF</Text>
</View>
)
}
}
class SearchViewController extends Component{
componentWillMount(){
console.log('search view controller will mounted');
}
render(){
return(
<View style={{flex:1, backgroundColor:'green'}}>
<Text>askfkjasljkdfjkla</Text>
</View>
)
}
}
etc..
Here is main tabbar Component class:
export default class SimpleClass extends Component{
constructor(props){
super(props);
this.state = {
selectedTab: 'news'
}
}
changeTab(selectedTab){
this.setState({selectedTab})
}
render(){
const { selectedTab } = this.state
const styles = {
backgroundColor: 'red'
};
return(
<TabBarIOS barTintColor="white"
unselectedItemTintColor="gray"
tintColor="red"
style={{flex:1}}
>
<TabBarIOS.Item
selected={selectedTab === 'news'}
title="Restaurants"
icon={require('./assets/restaurants.png')}
onPress={() => this.changeTab('news')}
>
<NavigatorIOS
style={styles.nav}
initialRoute={{
component: RestaurantsComponent,
title : 'Restaurants'
}}
/>
</TabBarIOS.Item>
<TabBarIOS.Item
title="Search"
selected={selectedTab === 'news2'}
onPress={() => this.changeTab('news2')}
icon={require('./assets/searchIco.png')}
>
<NavigatorIOS
style={styles.nav}
initialRoute={{
component: AnotherComponent,
title : 'Search'
}}
/>
</TabBarIOS.Item>
...
.../>
Here is the Component in navigationItem for Restaurants
And for other else:
I'vent cut the tabBar item for the screenshot but the TabBarIOS is successfully works if you mind it.
Is there any bug which is currently which cause from me or what happens to navigationItem's title attributes?
I've found my answer by the way I've not figured out what was happening in here but when looking into documentation and some articles, the use of NavigatorIOS is currently making mess.And there is a cool question & answer that I think its important to get idea of createNavigator... .
Here is the link.
There is a close approach for using TabBar, Navigation etc which are named createStackNavigator and createBottomTabNavigator. As the names tell us, createStackNavigator is currently work like UINavigationController and also createBottomTabNavigator is working like UITabBarController. So this is the basic implementation of these approach.
const firstTabStack = createStackNavigator({
HomeAlways: {
navigationOptions:{
title:'WASSUP1'
},
screen:BooksNav
}
})
const secondTabStack = createStackNavigator({
HelpAlways: {
navigationOptions:{
title:'WASSUP2'
},
screen:AddBook
}
})
And finally here we come with Tab implementation.
const Tab = createBottomTabNavigator({
Home: {
screen: firstTabStack,
navigationOptions:{
title:'title1'
}
},
Another: {
screen: secondTabStack,
navigationOptions:{
title:'title2'
}
}
});
What did I do with these code?
For iOS Developers to get understand what is going on in there, there is a 2 Controller (UIViewController or Component in RN), and these have different UINavigationController's and also different titles. And all of these controllers will going to stack of the UITabBarController' viewControllers.
The images in below are proof of the successfully running.
,
Im following this tutorial https://reactnavigation.org/docs/intro/ and im running into a bit of issues.
Im using the Expo Client app to render my app every time and not a simulator/emulator.
my code is seen down below.
I originally had the "SimpleApp" const defined above "ChatScreen" component but that gave me the following error:
Route 'Chat' should declare a screen. For example: ...etc
so I moved the decleration of SimpleApp to just above "AppRegistry" and that flagged a new error
Element type is invalid: expected string.....You likely forgot to export your component..etc
the tutorial did not add the key words "export default" to any component which I think it may have to do with the fact that im running it on the Expo app? so I added "export default" to "HomeScreen" and the error went away.
The new error that I cant seem to get rid off(based on the code below) is the following:
undefined is not an object (evaluating 'this.props.navigation.navigate')
I can't get rid of it unless I remove the "{}" around "const {navigate}" but that will break the navigation when I press on the button from the home screen
import React from 'react';
import {AppRegistry,Text,Button} from 'react-native';
import { StackNavigator } from 'react-navigation';
export default class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text>Hello, Chat App!</Text>
<Button
onPress={() => navigate('Chat')}
title="Chat with Lucy"
/>
</View>
);
}
}
class ChatScreen extends React.Component {
static navigationOptions = {
title: 'Chat with Lucy',
};
render() {
return (
<View>
<Text>Chat with Lucy</Text>
</View>
);
}
}
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen },
});
AppRegistry.registerComponent('SimpleApp', () => SimpleApp);
Additional Info:
When you are nesting child components, you need to pass navigation as prop in parent component.
//parent.js
<childcomponent navigation={this.props.navigation}/>
And you can access navigation like this
//child.js
this.props.navigation.navigate('yourcomponent');
Reference: https://reactnavigation.org/docs/en/connecting-navigation-prop.html
With Expo you should't do the App registration your self instead you should let Expo do it, keeping in mind that you have to export default component always:
Also you need to import View and Button from react-native: please find below the full code:
import React from 'react';
import {
AppRegistry,
Text,
View,
Button
} from 'react-native';
import { StackNavigator } from 'react-navigation';
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text>Hello, Chat App!</Text>
<Button
onPress={() => navigate('Chat', { user: 'Lucy' })}
title="Chat with Lucy"
/>
</View>
);
}
}
class ChatScreen extends React.Component {
// Nav options can be defined as a function of the screen's props:
static navigationOptions = ({ navigation }) => ({
title: `Chat with ${navigation.state.params.user}`,
});
render() {
// The screen's current route is passed in to `props.navigation.state`:
const { params } = this.props.navigation.state;
return (
<View>
<Text>Chat with {params.user}</Text>
</View>
);
}
}
const SimpleAppNavigator = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen }
});
const AppNavigation = () => (
<SimpleAppNavigator />
);
export default class App extends React.Component {
render() {
return (
<AppNavigation/>
);
}
}
As Bobur has said in his answer, the navigation prop isn't passed to children of the routed component. To give your components access to navigation you can pass it as a prop to them, BUT there is a better way.
If you don't want to pass the navigation prop all the way down your component hierarchy, you can use useNavigation instead. (Which in my opinion is just cleaner anyways, and reduces the amount of code we have to write):
function MyBackButton() {
const navigation = useNavigation();
return (
<Button
title="Back"
onPress={() => {
navigation.goBack();
}}
/>
);
}
https://reactnavigation.org/docs/use-navigation/
This is just really nice because if you have multiple levels of components you wont have to continuously pass the navigation object as props just to use it. Passing navigation just once requires us to 1. Add a prop to the component we want to pass it to. 2. Pass the prop from the parent component. 3. Use the navigation prop to navigate. Sometimes we have to repeat steps 1 and 2 to pass the prop all the way down to the component that needs to use navigation. We can condense steps 1 and 2, no matter how many times they are repeated, into a single useNavigation call with this method.
I think it is best.
Try this Code: onPress={() => this.props.navigation.navigate('Chat')}
<ChildComponent navigation={props.navigation} {...data} />
This will make the navigation from the parent propagated to the subsequent child navigations.
const AppNavigation =()=>{ <SimpleApp />}
export default class App extends React.Componet{
render(){
return (
<AppNavigation/>
);
}
}
I'm creating a custom Navigator component. I need to provide Navigator's stack components a navigator prop to allow them to push and pop scenes like this:
this.props.navigator.push(<Product id='4815'>)
this.props.navigator.pop()
In order to achieve this result, inside my Navigator's class, I've used React.cloneElement():
class Navigator extends Component {
constructor(props) {
super(props)
this.state = { stack: [this._addNavigator(props.children)] }
}
_addNavigator(scene) {
return React.cloneElement(scene, {
navigator: {
push: this.push,
pop: this.pop,
popToRoot: this.popToRoot
}
})
}
push(scene) {
this.setState({
stack: [...this.state.stack, this._addNavigator(scene)]
})
}
...
}
Everything works just fine, except for a particular scenario.
class App extends Component {
constructor(props) {
super(props)
this.state = { toggle: false }
}
render() {
return (
<View>
<TouchableOpacity onPress={() => {
this.setState({ toggle: !this.state.toggle })
}}>
<Text>Toggle</Text>
</TouchableOpacity>
<Navigator>
<SampleScene backgroundColor={this.state.toggle ? 'green' : 'black'} />
</Navigator>
</View>
)
}
When I pass some mutable prop to the Navigator children, as soon as the prop changes, the child component does not rerender. In the example above, SampleScene's backgroundColor stays black (because App class initial state for toggle is set to false), despite the user pressing the Toggle button. It seems like the SampleScene's render() method is called just once. How could I troubleshoot this behaviour?
Problem solved. Inside Navigator, I had to intercept new props via componentWillReceiveProps. Setting the stack to newProps' children via setState method made the Navigator rerender properly.
As a newbie in react-native i'm trying find a way to use react-navigation with different files without a navbar to navigate (for example just click on a button in the login view to enter the main view, clicking on log out button return to login view).
With react-navigation i made a global component 'Nav' and i placed in the main AppRegistry call:
AppRegistry.registerComponent('myApp', () => Nav);
While in the Navigator.js:
export const Nav = StackNavigator({
Login: { screen: LoginView },
AddStream: { screen: AddStreamView },
});
When i run
react-native run-android
i get the first view loaded under a white space (it should be the navigator, that i don't want in my app)
Now how can i remove the Nav from the view and how can i change View onPress a button component in another file?
There is another package that fill better my desire?
From the docs, setting the the headerMode to none should do the trick. This will hide the header on all of the screens defined in that StackNavigator. Example:
const Nav = StackNavigator({
Login: { screen: LoginView },
AddStream: { screen: AddStreamView },
},{
headerMode: 'none',
});
If you want to control the visibility of the header so that it appears on some of the screens but not all of them, then you can set headerMode to screen and for each individual screen set [the navigation prop for header: { visible: false }. See docs here. For example, if you are creating the screen component in another file, it might look like this:
// In the main file:
const Nav = StackNavigator({
Login: { screen: LoginView },
AddStream: { screen: AddStreamView },
},{
headerMode: 'screen',
});
// Hidden for your Login screen:
export default class LoginView extends Component {
static navigationOptions = {
header: { visible: false }
}
// etc...
}
// Visible for your AddStream screen:
export default class AddStreamView extends Component {
static navigationOptions = {
header: { visible: true }
}
// etc...
}
Edit: missed the second part of your question.
For navigating to a different screen, you really should go through the Hello World tutorial as it explains it. Specifically, this section. Relevant example code from the tutorial:
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Text>Hello, Chat App!</Text>
<Button
onPress={() => navigate('Chat')}
title="Chat with Lucy"
/>
</View>
);
}
In your case, 'Chat' would be either 'Login' or 'AddStream'.