Problem
I am making a very basic app that lets users post, and then renders the posts. I want to add some tabs, so I added TabNavigator. I am really confused because no tabs are showing up on my screen when I run the app, and I don't know why. I think I may be missing something, but I don't know what. I am really new to react native and firebase, and I would love any help on solving this problem.
Code
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image, TextInput, ScrollView, TouchableHighlight, Button, FlatList } from 'react-native';
import { Font } from 'expo';
import * as firebase from 'firebase';
import { TabNavigator } from 'react-navigation';
//Removed code - some random variables
export default class FeedView extends React.Component {
static navigationOptions = {
tabBarLabel: 'Home',
};
//Removed code - some more random variables
render() {
static navigationOptions = {
tabBarLabel: 'Home',
};
return (
//Removed code - Code for this section
);
}
class Comments extends React.Component {
static navigationOptions = {
tabBarLabel: 'Notifications',
};
render() {
static navigationOptions = {
tabBarLabel: 'Comments',
};
return (
//Removed code - This section's code
);
}
}
//Removed code - Styles declared here
const MyApp = TabNavigator({
Feed: {
screen: FeedView,
},
CommentScreen: {
screen: Comments,
},
}, {
tabBarPosition: 'top',
animationEnabled: true,
tabBarOptions: {
activeTintColor: '#fe8200',
},
});
Related
In my React-native project in the HomeScreen, I get some values from AsyncStorage. After getting this value I compare it and take decision in which screen it will go next.
If the getValue is null then it will go the WelcomeScreen and if it is not null then it will go the HomeDrawer Screen.
Here I have provided the code-
import React from 'react';
import { StyleSheet, Text, View, AsyncStorage } from 'react-native';
import {StackNavigator} from 'react-navigation';
import WelcomeScreen from './WelcomeScreen';
import LoginScreen from './components/LoginScreen';
import NoteMeHome from './components/NoteMeHome';
import HomeDrawer from './HomeDrawer/HomeDrawer';
import SettingsScreen from './components/SettingsScreen';
class HomeScreen extends React.Component {
state = {
getValue: '',
}
async componentDidMount() {
const token = await AsyncStorage.getItem('toke');
this.setState({ getValue: token });
}
render() {
console.log('#ZZZ:', this.state.getValue);
if(this.state.getValue !== null) {
return (
<AppStackNavigator/>
);
} else {
return (
<AppStackNavigator2/>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
const AppStackNavigator = new StackNavigator({
HomeDrawer: {screen:HomeDrawer},
WelcomeScreen: {screen:WelcomeScreen},
LoginScreen: {screen:LoginScreen},
NoteMeHome: {screen:NoteMeHome},
SettingsScreen: {screen:SettingsScreen}
})
const AppStackNavigator2 = new StackNavigator({
WelcomeScreen: {screen:WelcomeScreen},
HomeDrawer: {screen:HomeDrawer},
LoginScreen: {screen:LoginScreen},
NoteMeHome: {screen:NoteMeHome},
SettingsScreen: {screen:SettingsScreen}
})
export default HomeScreen;
Now, after running this, if I get null value in the variable getValue , then it is showing the following warning-
Warning: Can't call setState(or forceUpdate) on an unmounted
component. this is a no-op, but it indicates a memory leak in your
application. To fix, cancel all subscription and asynchronous tasks in
the componentWillUnmount method.
So, how can I solve this warning issue?
I don't know whether it's a good practice or not. The problem was- my component was initializing with empty string and I was checking for null in render function. Initializing getvalue with null or checking for empty string in render would solve this issue.
So, the change I made in my code is -
state = {
getValue: ''
}
And it removes the warning.
A better solution would be to use the SwitchNavigator from react-navigation since your navigation stacks are identical and you only want to route to the first screen based on that token.
see example
import React from 'react';
import { StyleSheet, Text, View, AsyncStorage } from 'react-native';
import {StackNavigator, createSwitchNavigator} from 'react-navigation';
import WelcomeScreen from './WelcomeScreen';
import LoginScreen from './components/LoginScreen';
import NoteMeHome from './components/NoteMeHome';
import HomeDrawer from './HomeDrawer/HomeDrawer';
import SettingsScreen from './components/SettingsScreen';
const AppStackNavigator = new StackNavigator({
HomeDrawer: {screen:HomeDrawer},
LoginScreen: {screen:LoginScreen},
NoteMeHome: {screen:NoteMeHome},
SettingsScreen: {screen:SettingsScreen}
});
export default createAppContainer(createSwitchNavigator(
{
LaunchScreen,
WelcomeScreen,
AppStackNavigator,
},
{
initialRouteName: 'LaunchScreen',
}
));
class LaunchScreen extends React.Component {
constructor(props) {
super(props);
this._getToken();
}
// Fetch the token from storage then navigate to the appropriate place
_getToken = async () => {
const tok = await AsyncStorage.getItem('toke');
// This will switch to the Welcome screen or main AppStack. Then this launch
// screen will be unmounted and thrown away.
this.props.navigation.navigate(tok ? 'AppStackNavigator' : 'WelcomeScreen');
};
// Render any loading content that you like here
render() {
return (
<View>
{/*...*/}
</View>
);
}
}
There is a Login.js module, with this module the user can log in. If the user data is correct, then the user switches to another Secured.js module. For switching from the module to the module I use StackNavigator. In the Secured.js module, Tabs should be displayed below. For this I use TabNavigator. The problem is that when I try to render tabs, it does not work for me, because React Native says that two navigators can not be created on one page. I tried to google, but I could not find anything worthwhile, except for the advice to update React Native, saying "this is a bug of the old version" (I tried too, but it didn't help). Is there any ideas how I can fix it?
Secured.js
import React, { Component } from 'react';
import {
ScrollView,
Text,
View,
Button,
StyleSheet,
FlatList
} from 'react-native';
// Import getNews function from news.js
import { getNews } from '../screens/news';
// We'll get to this one later
import Article from '../components/Article';
import Tabs from '../screens/Tabs';
export default class Secured extends React.Component {
static navigationOptions = {
title: "Neuigkeiten",
headerTintColor: "#FF0000",
headerLeft: null,
};
constructor(props) {
super(props);
this.state = { articles: [], refreshing: true };
this.fetchNews = this.fetchNews.bind(this);
}
// Called after a component is mounted
componentDidMount() {
this.fetchNews();
}
fetchNews() {
getNews()
.then(articles => this.setState({ articles, refreshing: false }))
.catch(() => this.setState({ refreshing: false }));
}
handleRefresh() {
this.setState(
{
refreshing: true
},
() => this.fetchNews()
);
}
render() {
return (
<View>
<FlatList
data={this.state.articles}
renderItem={({ item }) => <Article article={item} />}
keyExtractor={item => item.url}
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh.bind(this)}
/>
<View><Tabs /></View>
</View>
);
}
}
Tabs.js
import React, { Component } from 'react';
import { createBottomTabNavigator } from 'react-navigation';
import Secured from '../screens/Secured';
import Page1 from '../screens/Page1';
import Page2 from '../screens/Page2';
import Page3 from '../screens/Page3';
export default createBottomTabNavigator({
/// Secured: {screen:Secured,},
Page1: {screen:Page1,},
Page2: {screen:Page2,},
Page3: {screen:Page3,}
});
Firstly, it's not all happening because of react-native version is not updated. it's all about react-native navigation wrong use.
Let me explain you with two examples, one is wrong way similar with your case and second one is highly prefer, correct way to use navigators.
In React Native Navigation,
WRONG WAY
export default App extends React.Component {
render() {
/* In the root component we are rendering the app navigator */
return <AppNavigator />;
}
}
const AuthenticationNavigator = createStackNavigator({
SignIn: SignInScreen,
ForgotPassword: ForgotPasswordScreen,
});
class AuthenticationScreen extends React.Component {
render() {
return (
<AuthenticationNavigator />
);
}
}
const AppNavigator = createSwitchNavigator({
Auth: AuthenticationScreen, // This screen renders a navigator!
Home: HomeScreen,
});
In a screen inside of the navigator we are rendering another navigator You should avoid this! It will have its own navigation state and be unable To interact with any parent navigator, eg: it would not know the route "Home" exists
CORRECT & HIGHLY RECOMMENDED WAY
export default App extends React.Component {
render() {
return <AppNavigator />;
}
}
const AuthenticationNavigator = createStackNavigator({
SignIn: SignInScreen,
ForgotPassword: ForgotPasswordScreen,
});
const AppNavigator = createSwitchNavigator({
/*
* Rather than being rendered by a screen component, the
* AuthenticationNavigator is a screen component
*/
Auth: AuthenticationNavigator,
Home: HomeScreen,
});
IN YOUR CASE
import React, { Component } from 'react';
import { createBottomTabNavigator } from 'react-navigation';
import Secured from '../screens/Secured';
import Page1 from '../screens/Page1';
import Page2 from '../screens/Page2';
import Page3 from '../screens/Page3';
const Tabnav = createBottomTabNavigator({
/// Secured: {screen:Secured,},
Page1: {screen:Page1,},
Page2: {screen:Page2,},
Page3: {screen:Page3,}
});
and return the Tabnav in render function , instead of export the default. because we can't export more than one in one place.
I'm fairly advanced in iOS Swift language, but very new in react native framework or javascript language. I also have tried to find the right tutorial for stack navigator for hours but I can't find it. I'm currently using this tutorial to learn the basic of react native stack navigation, and would like to split the two screens into their own files. Basically, I want to mimic Swift with its AppDelegate.swift and View Controller's files. But it generates error like this:
Invariant Violation: Element type is invalid: expected a string (for
built-in components) or a class/function (for composite components)
but got: object. You likely forgot to export your component from the
file it's defined in, or you might have mixed up default and named
imports.
That was old error. The new error now:
TypeError: undefined is not an object (evaluating 'this.props.navigation.navigate')
This error is located at:
in HomeScreen (at renderApplication.js:33)
...
Here's my current code. This has been edited according to the solutions.
App.js:
import React from 'react';
import { Button, AppRegistry } from 'react-native';
import { StackNavigator } from 'react-navigation';
import HomeScreen from './HomeScreen';
import ProfileScreen from './ProfileScreen';
export default HomeScreen;
const App = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
});
AppRegistry.registerComponent("TestProject", () => App);
HomeScreen.js:
import React from 'react';
import { 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 (
<Button
title="Go to Jane's profile"
onPress={() =>
navigate('Profile', { name: 'Jane' })
}
/>
);
}
}
ProfileScreen.js:
import React from 'react';
import { Text } from 'react-native';
import { StackNavigator } from 'react-navigation';
export default class ProfileScreen extends React.Component {
static navigationOptions = {
title: 'Jane Profile',
};
render() {
const { navigate } = this.props.navigation;
return (
<Text>Welcome to Jane profile!</Text>
);
}
}
I would greatly appreciate any point out to any error and mistake in my code, as for now I'm completely oblivious to any problems in my code, as I'm learning this by myself. Please help. The first baby steps are always the hardest part every time I learn new language.
In this case, I'm trying to create a home screen with a button that leads to a second screen (profile) with a text in it. I'm also aware that I should be able to extract the name of the profile in the ProfileScreen which the HomeScreen sent, but I hardcoded it right now to make things simple for me to understand the solution.
Try this
import React from 'react';
import { StackNavigator } from 'react-navigation';
import HomeScreen from './HomeScreen';
import ProfileScreen from './ProfileScreen';
const App = StackNavigator({
HomeScreen: { screen: HomeScreen },
ProfileScreen: { screen: ProfileScreen },
}, {
initialRouteName: 'HomeScreen',
headerMode: 'none'
});
export default () => <App />;
The name of the route should be same not mandatory but
recommended.
Export the navigation element.
I don't even have a AppRegistry script in my project but it is still working.
If any issue comment that down below! Hope this will fix the issue
Note : This will work in react-navigation version 1.5.11
For react-navigation v2 try this code
import React from 'react';
import { createStackNavigator } from 'react-navigation';
import HomeScreen from './HomeScreen';
import ProfileScreen from './ProfileScreen';
const App = createStackNavigator({
HomeScreen: { screen: HomeScreen },
ProfileScreen: { screen: ProfileScreen },
}, {
initialRouteName: 'HomeScreen',
headerMode: 'none'
});
export default () => <App />;
Make the navigation script file global to access its props!
In react-native we don't register every component using AppRegistry.registerComponent, rather we register the parent component and parent component render child component. The changes that you need is to register the App component and export the HomeScreen and ProfileScreen.
Sample
App.js
...
const App = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
});
AppRegistry.registerComponent('projectName', () => App)
HomeScreen.js
...
class HomeScreen extends React.Component {
...
}
export default HomeScreen;
ProfileScreen.js
...
class ProfileScreen extends React.Component {
...
}
export default ProfileScreen;
Hope this will help!
I have a react native app with a DrawerNavigator. For the menu I have my own component. That works great.
Now I want to add a second side menu on the right side.
Is it possible to have two DrawerNavigator like in the Slack App?
This solution is not working for me: https://snack.expo.io/ry7lYempe
because I don't want to have a TabController as parent. Both Drawer should be accessible in all screens.
My code looks like this:
import React from 'react'
import reducer from './src/reducers'
import { DrawerNavigator } from 'react-navigation';
import SidebarMenu from './src/components/layout/SidebarMenu'
import { createStore } from 'redux';
import { Provider } from 'react-redux';
let store = createStore(reducer);
import News from './src/screens/News'
import HowTo from './src/screens/HowTo'
export default class MyApp extends React.Component {
render() {
return (
<Provider store={store}>
<MainNavigator />
</Provider>
);
}
}
const MainNavigator = DrawerNavigator(
{
News: {
path: '/news',
screen: News
},
HowTo: {
path: '/howto',
screen: HowTo
}
},
{
initialRouteName: 'News',
drawerPosition: 'left',
contentComponent: SidebarMenu
}
);
Solved after updating react-navigation to the newest version.
you can add any drawer you want, take a look at this exemple https://snack.expo.io/BJoChzewM
In your case, you may add your "MainNavigator" in another DrawerNavigator Component. don't forget to set drawerOpenRoute/drawerCloseRoute to prevent any side effects.
#KamleshKatpara Here is my solution (I nested the two navigators):
const DrawerExample = DrawerNavigator(
{
Home: {
path: '/',
screen: Home
}
},
{
initialRouteName: 'Home',
drawerPosition: 'left',
contentComponent: SidebarMenu
}
);
const MainDrawerExample = DrawerNavigator({
Drafts: {
screen: DrawerExample,
}
}, {
drawerPosition: 'right',
contentComponent: BookmarkMenu,
drawerOpenRoute: 'DrawerRightOpen',
drawerCloseRoute: 'DrawerRightClose',
drawerToggleRoute: 'DrawerRightToggle'
});
I'm trying to practice with different screens on a React Native project. Here's my code from App.js file.
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View
} from 'react-native';
import { StackNavigator } from 'react-navigation';
class HomeScreen extends React.Component {
static navigationOptions = {
title: "welcome",
};
render() {
return <Text style={{ color: 'black '}}>Hello, Navigation!</Text>;
}
}
const navigation = StackNavigator({
Home: { screen: HomeScreen },
});
export default class App extends Component<{}> {
render() {
return <navigation/>;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
});
When I run react-native run-android I get an Invariant Violation which tells me "to view config not found or name navigation". And then all the sites where this violation happens.
Help please thanks.
The name of any component must be capitalized. In your name, the component "navigation" is not capitalized. It should be "Navigation".
In react native the component's name should begin with capital letter, so it will be:
const Navigation = StackNavigator({
Home: { screen: HomeScreen },
});
and call it with capital:
export default class App extends Component<{}> {
render() {
return <Navigation/>;
}
}