Routing with React-Native - javascript

I have a HomePage.js with only one button, Login Button, and when clicked I want to render LoginPage.js. I tried something like this but it's not working:
HomePage.js:
import React, { Component } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import LoginPage from './LoginPage';
class HomePage extends Component{
constructor () {
super();
this.state = {LoginPage:false};
}
showLogin = () => {
this.setState({LoginPage:true});
}
render() {
return(
<View>
<TouchableOpacity onPress={() => this.showLogin()}>
<Text>LogIn</Text>
</TouchableOpacity>
</View>
)
}
}
export default HomePage;
In React it's easily done with react-router but I don't know how to do it with React-Native.
EDIT 1 after including react-navigation I get the following error: Route 'navigationOptions' should declare a screen. Did I miss something?
App.js:
import React, {Component} from 'react';
import {StackNavigator} from 'react-navigation';
import HomePage from './HomePage';
import LoginPage from './LoginPage';
const App = StackNavigator({
Home: {screen: HomePage},
LoginPage: {screen: LoginPage},
navigationOptions: {
header: () => null,
}
});
export default App;
HomePage.js
import React, { Component } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import LoginPage from './LoginPage';
class HomePage extends Component{
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
return(
<View>
<TouchableOpacity onPress={() => navigate('LoginPage'), { name: 'Login' }}>
<Text>Login</Text>
</TouchableOpacity>
<Button
onPress={() => navigate('LoginPage'), { name: 'Login' }}
>Log In</Button>
</View>
)
}
}
export default HomePage;
LoginPage.js
import React, { Component } from 'react';
import { Text, View } from 'react-native';
class LoginPage extends Component {
static navigationOptions = ({navigation}) => ({
title: navigation.state.params.name,
});
render() {
return (
<View>
<Text>This is login page</Text>
</View>
);
}
}
export default LoginPage;

Use React Navigation. Its the best and simple solution right now for react native.
for adding the library use npm install --save react-navigation
You can define class for routing like below using a StackNavigator.
import {
StackNavigator,
} from 'react-navigation';
const BasicApp = StackNavigator({
Main: {screen: MainScreen},
Profile: {screen: ProfileScreen},
, {
headerMode: 'none',
}
});
Then u can define classes like the one u mentioned in the question.
eg:
class MainScreen extends React.Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
const { navigate } = this.props.navigation;
return (
<Button
title="Go to Jane's profile"
onPress={() =>
navigate('Profile', { name: 'Jane' })
}
/>
);
}
}
and second class
class ProfileScreen extends React.Component {
static navigationOptions = ({navigation}) => ({
title: navigation.state.params.name,
});
render() {
const { goBack } = this.props.navigation;
return (
<Button
title="Go back"
onPress={() => goBack()}
/>
);
}
}
The navigate function can be used for navigating to a class and goBack can be used for going back to a screen.
For hiding header u can use header mode or specify navigation options in each screen
For details please refer : https://reactnavigation.org/

If you're just looking for a plugin like react-router to use on React Native, you can use react-native-router-flux, available here: https://github.com/aksonov/react-native-router-flux.
Also, it's better if you just put a reference to the function in there instead of invoking it. Something like:
<TouchableOpacity onPress={this.showLogin}>...</TouchableOpacity>

Related

React Native drawer navigation: how to open drawer by clicking custom button in specific scene?

I've tried to open the drawer by clicking the following button (screenshot) in a specific scene.
current status
and here goes my code.
// App.js
import React, { Component } from 'react';
import { createAppContainer, createSwitchNavigator } from 'react-navigation'
import { createStackNavigator } from 'react-navigation-stack'
import { createDrawerNavigator } from 'react-navigation-drawer'
import SplashScreen from 'react-native-splash-screen'
import RNBootSplash from "react-native-bootsplash"
import MainCategory from './src/Scenes/MainCategory'
import SubCategory from './src/Scenes/SubCategory'
import DetailScene from './src/Scenes/DetailScene'
import Questions from './src/Scenes/Questions'
import Ruleta from './src/Scenes/Ruleta'
import CustomDrawerComponent from './src/Components/CustomDrawerComponent'
export default class MainRouter extends Component {
componentDidMount() {
SplashScreen.hide()
RNBootSplash.hide({ duration: 250 })
}
render() {
const StackNavigator = createStackNavigator({
MainCategory: {screen: MainCategory},
SubCategory: {screen: SubCategory},
DetailScene: {screen: DetailScene},
Questions: {screen: Questions},
Ruleta: {screen: Ruleta},
}, {
initialRouteName: "MainCategory",
headerMode: "none"
})
const DrawerNavigator = createDrawerNavigator({
MainCategory: {screen: MainCategory},
}, {
drawerPosition: 'right',
contentComponent: CustomDrawerComponent
})
const MainNavigator = createSwitchNavigator({
Stack: StackNavigator,
Drawer: DrawerNavigator,
}, {
initialRouteName: 'Stack',
contentComponent: CustomDrawerComponent
})
const AppContainer = createAppContainer(MainNavigator)
return(
<AppContainer />
)
}
}
// Specific scene
import React, { Component } from 'react'
import { ScrollView, StatusBar, View, TouchableOpacity, Text, Image, Modal } from 'react-native'
import HeaderBar from '../Components/HeaderBar'
export default class Questions extends Component {
constructor(props) {
super(props)
this.state = {
headerTitle: props.navigation.getParam('headerTitle'),
catId: props.navigation.getParam('catId'),
}
}
openSideMenu = () => {
this.props.navigation.toggleDrawer
this.props.navigation.openDrawer()
}
render() {
return (
<View style={rootStyle}>
<View>
<StatusBar hidden={false} translucent backgroundColor="transparent" barStyle="light-content" />
<HeaderBar title={headerTitle} onPressLeft={() => this.props.navigation.goBack()} leftShow={true} onPressRight={this.openSideMenu} rightShow={true} />
</View>
<ScrollView>
<QuestionList todoQues={todoQues} favQues={favQues} action={this.action.bind(this)} />
</ScrollView>
</View>
)
}
}
But it's not working.
I expect the result like the following screenshot.
expected result
Please let me know what I should do more and how to use the drawer navigator in a specific scene.
Thanks for your time!
First of all instead of calling the method this.props.navigation.toggleDrawer(), call the method this.props.navigation.dispatch(DrawerActions.openDrawer()).
You also need to import DrawerActions for this to work. And remove the first function that you called in your openSideMenu function because you don't need the this.props.navigation.openDrawer()

Unable to render drawer navigation in React navigation?

Hi Have a react navigation method which make a drawer, which is inside a createSwitchNavigator. But when doing the final call I am not able to render the drawer correctly. I am getting
Invariant violation error coming.
This is my drawer:
const MyDrawerNavigator = createDrawerNavigator({
Home:{
screen: DashboardScreen,
},
Notifications: {
screen: UserMeetingScreen,
},
});
const MyApp = createAppContainer(MyDrawerNavigator);
export default MyApp
This is my stacknavigator
import AppStack from '../navigator/loggedIn';
import AuthStack from '../navigator/loggedOut';
import LoadingScreen from '../components/Loading/loading.component';
export const GetAppStack = createAppContainer(createSwitchNavigator(
{
AuthLoading: LoadingScreen,
LoggedOut: AuthStack,
LoggedIn: AppStack,
},
{
initialRouteName: 'AuthLoading',
}
))
And this is my dashboard component
export default class DashboardScreen extends React.Component{
static navigationOptions = {
headerMode: 'none',
headerStyle:{
backgroundColor: '#4F6D7A',
elevation: 0
}
}
render(){
return (
<View>
<OfflineNotice />
<Icon
name= 'flight-takeoff'
onPress= {()=> this.props.navigation.openDrawer()}
></Icon>
</View>
)
}
}
I am getting a Element type is invalid error. Check render method of Dashboard screen. What am I doing wrong?

undefined is not an object '_this3.props.navigation()' error in react native

running my react app gives error in navigationOptions() but it is working fine in render() function
App.js
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
import AppNavigator from './routs.js'
class App extends Component {
render() {
return (
<AppNavigator />
)
}
}
export default App
routs.js
import React from 'react'
import Home from './home.js'
import Phone from './phone.js'
import PhoneScreen from './phoneScreen.js'
import {createStackNavigator, createAppContainer} from 'react-navigation';
const MainNavigator = createStackNavigator({
home: {screen: Home},
add: {screen: Phone},
userScreen: {screen: PhoneScreen},
});
const AppNavigator = createAppContainer(MainNavigator);
export default AppNavigator;
home.js
import React from 'react'
import { TouchableOpacity, Text, View, TouchableHighlight, StyleSheet, Button } from 'react-native';
import { Actions } from 'react-native-router-flux';
import {AsyncStorage} from 'react-native';
class Home extends React.Component {
constructor(props) {
super(props);
}
static navigationOptions = {
headerTitle: 'Contacts',
headerRight: (
<Button
onPress={() => this.props.navigation.navigate('add')}
title="create new contact"
color="#000000"
size="20"
/>
),
};
}
export default Home;
"undefind is not an object(evaluating '_this3.props.navigation')"
please give solution
From the React Navigation Docs:
The binding of this in navigationOptions is not the HomeScreen
instance, so you can't call setState or any instance methods on it.
This is pretty important because it's extremely common to want the
buttons in your header to interact with the screen that the header
belongs to.
So, as you can see, this is not what you think it is in this case. Here's more from the docs detailing a working example:
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: <LogoTitle />,
headerRight: (
<Button
onPress={navigation.getParam('increaseCount')}
title="+1"
color="#fff"
/>
),
};
};
componentDidMount() {
this.props.navigation.setParams({ increaseCount: this._increaseCount });
}
state = {
count: 0,
};
_increaseCount = () => {
this.setState({ count: this.state.count + 1 });
};
/* later in the render function we display the count */
}
As you can see, changing navigationOptions from an object into a function allows you to grab the navigation reference. From there you can successfully navigate.

Calling a function in one of the components in the route

Suppose I have this:
const navtest = StackNavigator({
Home: {screen: HomeScreen},
Stuff: {screen: Screen2},
Stuff2: {screen: Screen3},
});
And I'm currently viewing Stuff2/Screen3. And I have some function in my HomeScreen component, say XYZ( ). On the basis of any certain action on my current route ( Screen3 ), how do I call that function XYZ( ) in HomeScreen?
My Routes file which directly links to index.js.
import {AppRegistry} from 'react-native';
import React, { Component } from 'react';
import {StackNavigator} from 'react-navigation';
import Screen2 from './Screen2';
import Screen3 from './Screen3';
import HomeScreen from './HomeScreen';
const navtest= StackNavigator({
Home: {screen: HomeScreen},
Stuff: {screen: Screen2},
Stuff2: {screen: Screen3},
});
AppRegistry.registerComponent('navtest', () => navtest);
and in my HomeScreen file
export default class HomeScreen extends Component {
constructor(props){
super(props);
this.state = {one: '0'};
}
static navigationOptions = {
header: null,
};
functionToPass() {
console.log('Function accessed');
this.setState({one: 'X'});
}
render() {
const {navigate} = this.props.navigation;
return (
<View>
<TouchableOpacity onPress = {() => navigate('Stuff', {ABC: 'abc'})}>
<Text>
{this.state.one}
</Text>
</TouchableOpacity>
</View>
);
}
}
Are you navigating from Home to Screen3? Then you can pass you function down as a param to be available in the navigation state. E.g. this.props.navigation.navigate(‘Stuff2’, { someFunc: someFuncFromHome }); then, in your Screen3, you can invoke the function like this: this.props.navigation.state.params.someFunc();
If Home is not the previous route, then maybe the function should be extracted outside of the Home component and use a redux action or similar things to achieve the same result.
Follow up:
There are a few ways that I think that may be able to help you.
1) Introduce Redux to your app. This is a good use case for redux as this state can be manipulated from anywhere in the app. Dispatch the same redux action from any where in the app, and the HomeScreen can read from redux.
2) If you don't want redux in your app yet, maybe you can keep the state in a navigator wrapper component like the one in my comment earlier.
class App extends Component {
constructor(props) {
super(props)
this.state = { one: '0' };
}
yourRootChangeFunc() {
this.setState({ one: 'X' });
}
render() {
return <App screenProps={{ rootChange: this.rootChange, one: this.state.one }} />
}
}
Then you can access one in other screens with this.props.screenProps.one
3) Another way is use setParams, which you can setParams for a specific screen by passing in the key of the route.
In HomeScreen component:
export default class HomeScreen extends Component {
...
componentWillMount() {
this.props.navigation.setParams({ one: '0' });
}
render() {
const { state: { params = {} } = this.props.navigation;
return (
...
<View>
{params.one || '0'} // this is because currently there is no way to set a default params for a route
</View>
...
);
}
}
Then in Screen2 component:
import React, { Component } from 'react';
import {
View,
TouchableOpacity,
Text,
} from 'react-native';
import { NavigationActions } from 'react-navigation';
export default class Screen2 extends Component {
onPress = () => {
const setParamsAction = NavigationActions.setParams({
params: { one: 'X' },
key: this.props.navigation.state.params.homeKey,
})
this.props.navigation.dispatch(setParamsAction)
}
render() {
return (
<View>
<TouchableOpacity onPress={this.onPress}>
<Text>Screen2</Text>
</TouchableOpacity>
</View>
);
}
}
This way, pressing on Screen2 text in Screen2 will change the params for HomeScreen. Then when go back to HomeScreen, we can see that the text changed from '0' to 'X'.

React Native Navigation Component Route Issue

New react native user here. I'm running into an issue and I am not sure how to proceed. I was able to get react-navigation running properly and then began receiving an error: "The component for route must be a a React Component" but unless I'm missing something, I believe that the component I am referencing is a react component. See my index.android.js below and my Home.js below:
//index.android.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import {
TabNavigator,
StackNavigator
} from 'react-navigation';
import Home from './app/components/Home/Home';
import Search from './app/components/Search/Search';
export default class demoApp extends Component {
render() {
return (
<SimpleNavigation/>
);
}
}
export const SimpleNavigation = StackNavigator({
Home: {
screen: Home,
header: { visible: false },
navigationOptions: {
title: 'Home',
header: null
},
},
Search: {
screen: Search,
navigationOptions: {
title: 'test'
},
},
},{});
//Home.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image,
TextInput,
Button,
TouchableHighlight
} from 'react-native';
class Home extends Component {
constructor(props){
super(props);
this.state = {zipCode: ''}
}
navigate = (zipCode) => {
this.props.navigation.navigate('Search', zipCode);
}
render() {
return (
<View style={styles.container}>
<View style={[styles.boxContainer, styles.boxOne]}>
<Image style={styles.logo} source {require('../../images/Logo.png')} />
<Text style={styles.title}>An application to do things</Text>
<TextInput
style={styles.textInput}
placeholder='Enter a Zip Code'
onChangeText={(zipCode) => this.setState({zipCode})}
>
</TextInput>
</View>
<View style={[styles.boxContainer, styles.boxTwo]}>
<TouchableHighlight onPress={() => this.navigate(this.state.zipCode)}>
<Text style={styles.searchBox}>
Search
</Text>
</TouchableHighlight>
</View>
</View>
);
}
}
Any help/react pointers much appreciated. Thank you!
I think the problem is with home.js since you aren't exporting it. Try this :
export default class Home extends Component { ... }
^^^^^^^^^^^^^^
Add those or just add
export default Home;
at the end of the home.js file
const MyNavigator = createStackNavigator({
RouteNameOne: {
screen: () => <HomeScreen/>
},
RouteNameTwo: {
screen: () => <NewScreen/>
}
}, {
initialRouteName: 'RouteNameOne'
});
It will work.
For anyone else coming here, you could be receiving the "The component for route must be a a React Component" error because you don't have a default export, which was the case for me.
export HomeScreen extends React.Component {
...
vs
export default HomeScreen extends React.Component {
...
Hope this helps someone!
In my case putting this code block inside home.js solved the issue
static navigationOptions = {
navigationOptions: {
title: "scren title",
}
};

Categories