Every single example I have seen in Stack Overflow uses the case where the App component is a functional component, but I am using a class-based App component and I am playing catch up with the breaking changes of React Navigation.
This is my code inside App.js file:
import React from "react";
import { StyleSheet, Text, View } from "react-native";
import {
createBottomTabNavigator,
createStackNavigator
} from "react-navigation";
import AuthScreen from "./screens/AuthScreen";
import WelcomeScreen from "./screens/WelcomeScreen";
import MapScreen from "./screens/MapScreen";
import DeckScreen from "./screens/DeckScreen";
import SettingsScreen from "./screens/SettingsScreen";
import ReviewScreen from "./screens/ReviewScreen";
export default class App extends React.Component {
render() {
const MainNavigator = createBottomTabNavigator({
welcome: WelcomeScreen,
auth: AuthScreen,
main: {
screen: createBottomTabNavigator({
map: MapScreen,
deck: DeckScreen,
review: {
screen: createStackNavigator({
review: ReviewScreen,
settings: SettingsScreen
})
}
})
}
});
return (
<View style={styles.container}>
<MainNavigator />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
justifyContent: "center"
}
});
I was refactoring it along nicely and then it broke with this message that I cannot figure out:
Invariant Violation: The navigation prop is missing for this
navigator. In react-navigation 3 you must set up your app container
directly.
Yes, I looked at React Native docs regarding AppContainer and I saw this line of code
const AppContainer = createAppContainer(AppNavigator);
but I am not sure how to refactor mines or not refactor it too much to get this right.
When I try to add this line of code export default createAppContainer(App); at the bottom of my file and obviously removing export default from in front of class App... and also pulling out createAppContainer from inside my react-navigation, I get an error that says:
TypeError: undefined is not an object
Set you createAppContainer like this
import AuthScreen from "./screens/AuthScreen";
import WelcomeScreen from "./screens/WelcomeScreen";
import MapScreen from "./screens/MapScreen";
import DeckScreen from "./screens/DeckScreen";
import SettingsScreen from "./screens/SettingsScreen";
import ReviewScreen from "./screens/ReviewScreen";
import { createBottomTabNavigator, createStackNavigator, createAppContainer } from 'react-navigation';
const MainNavigator = createAppContainer(createBottomTabNavigator({ // set createAppContainer here
welcome: WelcomeScreen,
auth: AuthScreen,
main: {
screen: createBottomTabNavigator({
map: MapScreen,
deck: DeckScreen,
review: {
screen: createStackNavigator({
review: ReviewScreen,
settings: SettingsScreen
})
}
})
}
}));
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<MainNavigator />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
justifyContent: "center"
}
});
Related
I learning React Native and I'm doing a simple View and setting the style to a container style, but nothing happens when I run the code. The View is now filling with the color read from the code. Its very starter code, just not seeing where its going wrong? When I run this, nothing in the view changes, it just white? I was expecting to see a screen of red?
import React, { useState } from 'react';
import {View, Text, StyleSheet, ScrollView} from 'react-native';
import { Input } from 'react-native-elements';
import SLSHeader from '../app-components/slsheader';
export default function Home (props) {
return (
<View styles={styles.container}>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'red'
}
});
The only place Im using it thus far is in a Navigator file... Its a switch Navigator with a Drawer Navigator...
import React from 'react';
import { createAppContainer, createSwitchNavigator} from 'react-navigation';
import { createDrawerNavigator } from 'react-navigation-drawer';
//import CustomDrawer from './CustomDrawer';
//Import Screens
import LoginScreen from '../screens/Login';
import HomeScreen from '../screens/Home';
//Drawer Screens
const DrawerScreens = {
Home: {
screen: HomeScreen,
navigationOptions:{
headerShown: false
}
}
}
const DrawerNavigator = createDrawerNavigator(DrawerScreens, {
defaultNavigationOptions: {
headerStyle: {
height: 60
}
},
//contentComponent: CustomDrawer
});
//Pre Login Screens
const PreLoginScreens = {
Login: {
screen: LoginScreen,
navigationOptions:{
headerShown: false
}
},
PostLogin: {
screen: DrawerNavigator
}
}
const PreloginNavigator = createSwitchNavigator(PreLoginScreens,{
defaultNavigationOptions: {}
});
export default createAppContainer(PreloginNavigator);
Yes, in the App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import SLSNavigator from './Navigation/SLSNavigator';
export default function App() {
return (
<SLSNavigator />
);
}
I am using React Navigation Version 4 and after setting my navigations and all screens and run the code I am facing the following issue:
TypeError: (0, _reactNavigation.default) is not a function.
My Routes.Js is -
import React from 'react';
import createAppContainer from 'react-navigation';
import { createStackNavigator, HeaderBackButton } from 'react-navigation-stack';
import { Drawer } from './Drawer';
import LoginScreen from '../screens/LoginScreen';
import InitialScreen from '../screens/InitialScreen';
import LogoutScreen from '../screens/LogoutScreen';
const RootStack = createStackNavigator(
{
Drawer: {
screen: Drawer
},
LoginScreen: {
screen: LoginScreen
},
LogoutScreen: {
screen: LogoutScreen
}
InitialScreen: {
screen: InitialScreen
}
},
{
initialRouteName: 'InitialScreen',
headerMode: "none"
}
)
const App = createAppContainer(RootStack);
export default App;
and Index.js is -
import React, { Component } from 'react';
import { StyleSheet, AsyncStorage } from 'react-native';
import { Button, Text, Drawer } from 'native-base';
import App from './config/Routes';
import AppHeader from './components/Header/Header';
export default class Index extends Component {
render() {
const { globalContainer } = styles;
return (
<App
style={ globalContainer }
navigation={this.props.navigation}>
</App>
)
}
}
Any help??
It appears from the documentation that it should be a named import, not a default.
import { createAppContainer } from 'react-navigation';
Please read the docs. It's also a good first place to look when debugging.
I am setting up a new route, and want to support about making new routes. What can I do?
My AppNavigator.js - Routes are in here
import { createStackNavigator, createAppContainer } from "react-navigation";
import Login from "../client/components/auth/Login";
import Register from "../client/components/auth/Register";
const AppNavigator = createStackNavigator({
Login: { screen: Login },
Register: { screen: Register }
});
const App = createAppContainer(AppNavigator);
export default App;
In App.js, I just imported AppNavigator
Register.js - I am trying to route to Login
import React from "react";
import {
StyleSheet,
Text,
View,
TextInput,
Button,
TouchableOpacity
} from "react-native";
import PropTypes from "prop-types";
export default class Register extends React.Component {
render() {
return (
<View style={styles.container}>
<Text onPress={() => this.props.navigation.navigate("Login")}>
Have account? Login
</Text>
</View>
);
}
}
Create a new stack with createStackNavigator. As far as I understand you want to redirect to another route after Log in. For that, you have to add them createSwitchNavigator. Then after successful login, you will redirect your route for example settings and for successful login you save the token in Asyncstorage. So, every time you load the app if you are successfully logged in you will be redirected to Setting page automatically otherwise the registration page will be loaded.
Here the code for App.js
App.js
import { createStackNavigator, createAppContainer,createSwitchNavigator } from "react-navigation";
import Login from "../client/components/auth/Login";
import Register from "../client/components/auth/Register";
import Setting from "../client/components/auth/Setting";
class AuthLoadingScreen extends React.PureComponent {
constructor(props) {
super(props);
this._bootstrapAsync();
}
async _getStoredDataFromAsync(key) {
return await AsyncStorage.getItem(key);
}
_bootstrapAsync = async () => {
const userToken = await this._getStoredDataFromAsync("userToken");
this.props.navigation.navigate(userToken ? 'App' : 'Some');
};
render() {
return (
<View style={{flex:1}}>
<ActivityIndicator />
</View>
);
}
}
const AppNavigator = createStackNavigator({
Login: { screen: Login },
Register: { screen: Register }
});
const SomeNavigator = createStackNavigator({
Setting: { screen: Setting},
});
const Navigation = createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: AppNavigator ,
Some: SomeNavigator ,
},
{
initialRouteName: 'AuthLoading',
});
const App = createAppContainer(Navigation );
export default App;
you can use react-native router flux for navigation
https://www.npmjs.com/package/react-native-router-flux
What I want to do is wrapp on the very top of my app a switch navigator named MainNavigator which redirect to a LoadingScreen who determine if user is Logged or no and navigate to AuthNavigator or AppNavigator.
AuthNavigator himself is a stackNavigator who display screens 'LoginScreen' or 'SignUpScreen'.
AppNavigator is a DrawerNavigator with multiple screens (Profile, Home...).
The problem I encouter is that the return method seems to work, because I can see the logs in my console from the 'LoginScreen' component but on the screen, the only return is a white screen.. When I call directly my 'LoginScreen' component from the LoadingScreen component by using :
_bootstrapAsync = async () => {
const userToken = await AsyncStorage.getItem('userToken');
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away. switched to develop the app
this.props.navigation.navigate(userToken ? 'App' : 'LoginScreen');
};
I had the LoginScreen component rendered on my device.
I know the problem come from my AuthNavigator and AppNavigator because I can return screens components from AuthLoading or MainNavigator
Here some code :
* Main Navigator: entry point of the app
* Redirect to AuthLoadingScreen by default,
* if user is signed (AsyncStorage) or successly signIn
* redirect to AppNavigator else if user isn't SignIn
* go to AuthNavigator
import { createAppContainer, createSwitchNavigator } from 'react-
navigation';
import AuthNavigator from './auth/AuthNavigator'
import AppNavigator from './app/AppNavigator'
import AuthLoadingScreen from '../screens/auth/AuthLoadingScreen'
export default createAppContainer(createSwitchNavigator(
{
// You could add another route here for authentication.
// Read more at https://reactnavigation.org/docs/en/auth-flow.html
AuthLoading: AuthLoadingScreen,
App: AppNavigator,
Auth: AuthNavigator,
},
{
initialRouteName: 'AuthLoading',
}
));
AuthLoading :
import React from 'react';
import {
ActivityIndicator,
AsyncStorage,
StatusBar,
StyleSheet,
View,
Text
} from 'react-native';
export default class AuthLoadingScreen extends React.Component {
constructor(props) {
super(props);
this._bootstrapAsync();
}
// Fetch the token from storage then navigate to our appropriate place
_bootstrapAsync = async () => {
const userToken = await AsyncStorage.getItem('userToken');
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away. switched to develop the app
this.props.navigation.navigate(userToken ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
return (
<View>
<Text>Loading</Text>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
);
}
}
AuthNavigator:
import { createStackNavigator } from 'react-navigation';
import LoginScreen from '../../screens/auth/LoginScreen';
import SignUpScreen from '../../screens/auth/SignUpScreen';
export default createStackNavigator({
Login : {
screen : LoginScreen
},
SignUp: {
screen : SignUpScreen
}
},
{
initialRouteName: 'Login',
})
LoginScreen:
import React, { Component } from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default class LoginScreen extends Component {
render() {
console.log('LOGIN SCREEN')
return (
<View style={styles.container}>
<Text style={styles.text}>Login</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text : {
fontSize: 20,
}
});
I can't find the error, but I know it comes from the AuthNavigator and AppNavigator. I can see the 'LOGIN SCREEN' or 'APP SCREEN' if I set the AuthLoading route to render App in my console but nothing on the screen, when I init a new route LoginScreen in my MainNavigator and navigate to that route from the AuthLoading it works, I have a beautiful text displayed on my screen.
I Know it's probably an insignifiant error but I spend hours to fix this by myself and can't find, so if someone can help me it would be nice!
Thanks by advance!
Problem solved... Because at the top of my app in App.js I wrapped my MainNavigator like that
<View>
<StatusBar hidden={true} />
<MainNavigator />
</View>
So I remove the view and statusbar... and it works. 4 days to figure it out... I love it! :')
How do I combine StackNavigator and TabNavigator?
My following code works:
index.android.js :
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
import { StackNavigator,TabNavigator } from 'react-navigation';
import TestComp1 from './src/components/TestComp1'
import TestComp2 from './src/components/TestComp2'
import TestComp3 from './src/components/TestComp3'
import TestComp4 from './src/components/TestComp4'
import TestComp5 from './src/components/TestComp5'
export default class myApp extends Component {
render() {
return (
<MyApp />
);
}
}
const MyApp = StackNavigator({
TestComp1: {screen:TestComp1},
TestComp2: {screen:TestComp2}
});
const Tabs = TabNavigator({
TestComp3: {screen:TestComp3},
TestComp4: {screen:TestComp4}
TestComp5: {screen:TestComp5}
});
AppRegistry.registerComponent('myApp', () => myApp);
This works only for StackNavigator. I want to keep the existing navigation and integrate TabNavigation. Now in TestComp1 if I do the following:
TestComp1 :
import { StackNavigator, TabNavigator } from 'react-navigation';
import { FooterTabs } from './routes/FooterTabs';
export default class HomePage extends Component {
static navigationOptions = {
header: null
};
render() {
return(
<View style={styles.container}>
<View style={styles.mainContent}>
<Button
onPress={() => this.props.navigation.navigate('TestComp1', {name: 'Lucy'})}
title="NewPage"
/>
<FooterTabs /> //Page shows all StackNavigator screens if I add this
</View>
</View>
)
}
}
And the FooterTabs:
import { StackNavigator, TabNavigator } from 'react-navigation';
import TestComp3 from '../TestComp3';
import TestComp4 from '../TestComp4';
import TestComp5 from '../TestComp5';
export const FooterTabs = TabNavigator({
Tab1: {
screen: TestComp3
},
Tab2: {
screen: TestComp4
},
Tab3: {
screen: TestComp5
},
})
The FooterTabs is shown but TestComp1 and TestComp2 are also shown everything below one another. How do I fix this? Thanks.
UPDATE 1:
UPDATE 2:
const Tabs = TabNavigator({
TestComp3: {screen:TestComp1},
TestComp4: {
screen:TestComp4,
navigationOptions: ({ navigation }) => ({
title: "TestComp4",
tabBarIcon: ({ tintColor }) => <MaterialIcons name="accessibility" size={20}/>
})
}
UPDATE 3
I added another const for DrawerNavigator and configured it like this:
const Drawer = DrawerNavigator({
First:{
screen: DrawerScreen1
},
Second:{
screen: DrawerScreen2
}
},{
initialRouteName:'First',
drawerPosition: 'left'
});
And included in the app:
const MyApp = StackNavigator({
TestComp1: {screen:TestComp1},
TestComp2: {screen:TestComp2},
Tabs: {
screen: Tabs
},
Drawer: {
screen: Drawer
},
}, {
initialRouteName: "Tabs"
});
I'm calling it :
<Button
onPress={() => this.props.navigation.navigate('DrawerOpen')}
title="Show Drawer"
/>
OnPress of this button the DrawerScreen1 is called but as a component, it does not show as a drawer from the left. What am I missing?
You can try this:
const Tabs = TabNavigator({
TestComp3: {screen:TestComp3},
TestComp4: {screen:TestComp4}
TestComp5: {screen:TestComp5}
});
const MyApp = StackNavigator({
TestComp1: {screen:TestComp1},
TestComp2: {screen:TestComp2},
Tabs: {
screen: Tabs
}
}, {
initialRouteName: "Tabs"
});
and remove <FooterTabs /> from TestComp1
Let's see now. What you need is first a StackNavigator then inside one of the screens of the StackNavigator you need a TabNavigator. Right?
The answer to this would be the following:
For your index.android.js:
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
import { StackNavigator } from 'react-navigation';
import TestComp1 from './src/components/TestComp1'
import TestComp2 from './src/components/TestComp2'
// Don't need to export default here since this is the root component
// that is registered with AppRegistry and we don't need to import it anywhere.
class myApp extends Component {
render() {
return (
<MyApp />
);
}
}
// Notice that these two screens will consist the navigation stack.
// Assume, TestComp1 contains our Tabbed layout.
const MyApp = StackNavigator({
TestComp1: { screen:TestComp1 }, // This screen will have tabs.
TestComp2: { screen:TestComp2 }
});
AppRegistry.registerComponent('myApp', () => myApp);
Let's now move on to your TestComp1, which is the screen that has your tabs.
TestComp1:
// ... all imports here.
// This should be your first tab.
class Home extends Component {
static navigationOptions = {
// Label for your tab. Can also place a tab icon here.
tabBarLabel: 'Home',
};
render() {
return(
<View style={styles.container}>
<View style={styles.mainContent}>
// This will change your tab to 'Profile'.
<Button
onPress={() => this.props.navigation.navigate('Profile', {name: 'Lucy'})}
title="NewPage"
/>
</View>
</View>
)
}
}
// This can be your second tab and so on.
class Profile extends Component {
static navigationOptions = {
// Label for your tab.
tabBarLabel: 'Profile',
};
render() {
return (
<Text>This is the profile Tab.<Text>
);
}
}
export default TabNavigator({
Home: {
screen: Home,
},
Profile: {
screen: Profile,
},
}, {
// This will get the tabs to show their labels/icons at the bottom.
tabBarPosition: 'bottom',
animationEnabled: true,
tabBarOptions: {
activeTintColor: '#e91e63',
},
});
So essentially, your TestComp1 has two components (Home and Profile) inside it which are both parts of TabNavigator so they'll act as tabs. (You don't need to worry about a separate footer component as ReactNavigation places that automatically using your component's navigationOptions) We'll be exporting this TabNavigator instance that we'll use as an import to index.android.js and we'll place this import inside our StackNavigator as a screen of the app.
This way you've incorporated Tabs as well as StackNavigator inside your RN app. Also, tabBarPosition: 'bottom' places your tabs at the bottom.
You also no longer to maintain a separate FooterTabs component as you can see.
Read up the docs if you need more clarity or fine-tuning.
Hope I've helped. :)