I'm new to React native and trying to follow tutorials to learn however can't get navigation between screens to work. I have previously got it working fine with one screen but when adding navigation I get errors.
Following this: https://facebook.github.io/react-native/releases/next/docs/navigation.html
Gives me code like this:
import React from 'react';
import {
StackNavigator,
} from 'react-navigation';
export default class HomeScreen 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' })
}
/>
);
}
}
class ProfileScreen extends React.Component {
static navigationOptions = {
title: 'Profile',
};
render() {
return (
<Button title="do nothing"/>
);
}
}
const App = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
});
Trying to run this (through the expo app) results in the error
undefined is not an object (evaluating 'this.props.navigation.navigate')
So am I doing navigation correctly and how do I fix this error?
You need to use AppRegistry.registerComponent in your App.
A nice way of doing this is to create a src directory and then create the 2 js files, HomeScreen and ProfileScreen there. Then create and App.js on the same level as your index.android.js or index.ios.js and include these 2 files and declare the StackNavigator as you did in your code, but instead of const App you need to have const NameOfYourApp, where NameOfYourApp is how you named your project when you ran create-react-native-app.(If it is App, just leave it like that)
Then you need to add this line at the end, AppRegistry.registerComponent('NameOfYourApp', () => NameOfYourApp);
import React from 'react';
import {
StackNavigator,
} from 'react-navigation';
import HomeScreen from './src/HomeScreen'
import ProfileScreen from './src/ProfileScreen'
const NameOfYourApp = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
});
AppRegistry.registerComponent('NameOfYourApp', () => NameOfYourApp)
The final step is to import your App.js file in your index.android.js or index.ios.js
import './App';
Related
In my scenario, I am having three different screens like Page1, Page2, Page3. Here, if the user last visited page 2 then next time if user open application, instead of showing page1 need to show page2. How to achieve this using a react-native application?
I tried by using async storage but don’t know how to manage multiple pages
AsyncStorage.getItem("alreadyLaunched").then(value => {
if(value == null){
AsyncStorage.setItem('alreadyLaunched', true); // No need to wait for `setItem` to finish, although you might want to handle errors
this.setState({firstLaunch: true});
}
else{
this.setState({firstLaunch: false});
}}) // Add some error handling, also you can simply do this.setState({fistLaunch: value == null})
App.js
import { createAppContainer } from 'react-navigation';
import { createStackNavigator} from 'react-navigation-stack';
import FirstPage from './pages/FirstPage';
import SecondPage from './pages/SecondPage';
import ThirdPage from './pages/ThirdPage';
//import all the screens we are going to switch
const App = createStackNavigator({
//Constant which holds all the screens like index of any book
FirstPage: { screen: FirstPage, header: null},
SecondPage: { screen: SecondPage, headerLeft: null, headerBackTitle: null},
ThirdPage: { screen: ThirdPage},
},
{
initialRouteName: 'FirstPage',
}
);
export default createAppContainer(App);
You should use AsyncStorage to store the current screen, so any time you move from a screen to another you should call
AsyncStorage.setItem('screen', 'nameOfYourScreen')
and in App.js you should call
AsyncStorage.getItem('screen');
and affect it to initialRouteName
PS: getItem() is asynchronous.
Create another page say "Decider.js" and also import createSwitchNavigator from react-navigation. and make it your default page every time app launches. and there we will check where was the user last time and navigate to that page.
Let achieve this as below:
App.js
import { createSwitchNavigator, createAppContainer } from 'react-navigation'; //new import createSwitchNavigator
import { createStackNavigator} from 'react-navigation-stack';
import FirstPage from './pages/FirstPage';
import SecondPage from './pages/SecondPage';
import ThirdPage from './pages/ThirdPage';
import Deccider from './pages/Decider.js'; // New deciding screen
//import all the screens we are going to switch
const App = createStackNavigator({
//Constant which holds all the screens like index of any book
FirstPage: { screen: FirstPage, header: null},
SecondPage: { screen: SecondPage, headerLeft: null, headerBackTitle: null},
ThirdPage: { screen: ThirdPage},
},
{
initialRouteName: 'FirstPage',
}
);
export default createAppContainer(createSwitchNavigator(
{
AuthLoading: Decider,
App: App,
},
{
initialRouteName: 'AuthLoading',
}
));
Now on every app launch first screen called will be Decider.js
Decide.js
import React from 'react';
import {
AsyncStorage,
View,
} from 'react-native';
export default class AuthLoadingScreen extends React.Component {
constructor() {
super();
this._bootstrapAsync();
}
_bootstrapAsync = async () => {
var alreadyLaunchedPage = await
AsyncStorage.getItem('alreadyLaunchedPage');
if (alreadyLaunchedPage) {
this.props.navigation.navigate(alreadyLaunchedPage);
} else {
this.props.navigation.navigate('App');
}
};
render() {
return (
<View style={{ flex: 1 }}>
{/* Any Loading or splash design */}
</View>
);
}
}
**On every page's (FirstPage,SecondPage,ThirdPage) componentDidMount **
...
componentDidMount(){
AsyncStorage.setItem("alreadyLaunchedPage","FirstPage")//if FirstPage
// AsyncStorage.setItem("alreadyLaunchedPage","SecondPage")//if SecondPage
// AsyncStorage.setItem("alreadyLaunchedPage","ThirdPage")//if ThirdPage
}
...
Hope it will solve your problem.
NOTE: Here we have used createSwitchNavigator to prevent going back to Decider.js once you reach any of the FirstPage, SecondPage or ThirdPage.
I am new to React Native and I don't understand how to solve this problem. I already installed react-native-gesture-handler.
I am getting this error in the command:
Accessing view manager configs directly off UIManager via UIManager['getConstants'] is no longer supported. Use UIManager.getViewManagerConfig('getConstants') instead.
This is a part of the code:
import { createAppContainer, DrawerNavigator } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import Home from "./src/screens/Home.js";
import first from "./src/screens/first.js";
import React from 'react';
class App extends React.Component{
render(){
return(
<RootStack/>
)
}
}
const RootStack = createStackNavigator ({
Home:{screen:Home,
navigationOptions: {
header: null
}},
first:{screen:first,
navigationOptions: {
header: null
}}
});
//const App = createAppContainer(RootStack);
export default App;
I think this way is better:
const AppNavigator = createSwitchNavigator({
Auth: {
screen: Auth
},
Root: {
screen: BottomTabNavigator
}
})
const AppContainer = createAppContainer(AppNavigator)
const App = () => {
return (
<AppContainer />
)
}
export default App
OR
if it didn't work then you better check your index.js file whether you imported App Component (Root component) correctly or not?
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.
I am currently working on a app which works with react native and I tried to make a flow using react-navigation working on this tutorial but I am having trouble at the point of running my project, I've done a lot of research and i just cant get to the solution, first for all I am using react-native-cli: 2.0.1 and react-native: 0.48.3, this is my code:
App.js:
import React, { Component } from 'react';
import { Text, Button, View } from 'react-native';
import {
StackNavigator,
} from 'react-navigation';
class App extends Component {
static navigationOptions = {
title: 'Welcome',
};
render() {
console.log(this.props.navigation);
const { navigate } = this.props.navigation;
return (
<View>
<Text>Go to maps!</Text>
<Button
onPress={() => navigate('Map')}
title="MAP"
/>
</View>
);
}
}
export default App;
my Router.js
import {
StackNavigator,
} from 'react-navigation';
import MapMarker from './MapMarker';
import App from './App';
export const UserStack = StackNavigator({
ScreenMap:
{
screen: MapMarker,
navigationOptions:
{
title: "Map",
header:null
},
},
ScreenHome:
{
screen: App,
navigationOptions:
{
title: "Home",
headerLeft:null
},
},
});
As you can see this is a pretty basic App which i just cant make work, this is a screenshot of my error on the simulator:
I would really really appreciate if you guys could help me with this.
Thanks.
You should change the code onPress={() => navigate('Map')} to onPress={() => navigate('ScreenMap')} and ScreenHome should be the first screen then ScreenMap as you want to navigate from App to MapMarker. Or you can set initialRouteName: ScreenHome in the stacknavigator.
You create your StackNavigator, but where do you use it? You should have something like
import React, { Component } from 'react';
import {
AppRegistry,
View
} from 'react-native';
import {
StackNavigator,
} from 'react-navigation';
export default class MyApp extends Component {
render() {
return (
<View style={{flex:1}}>
<StackNavigator/>
</View>
);
}
}
AppRegistry.registerComponent('MyApp', () => MyApp);
Now that your StackNavigator is controlling what is shown, your App component will have navigation in its props. Note, you do not "pass" the navigation prop. This is handled for you.
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'.