I am trying to use navigation in react native, but it gives me an error when I try use a component with navigate (LogIn) with the
<Login/>
tags.
It says that navigate is undified so I passed the navigation as a prop with no success
App.js
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { SafeAreaView, StyleSheet, Text, View } from 'react-native';
import Login from "./assets/code/Login.js";
import { NavigationContainer, StackActions } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import 'react-native-gesture-handler';
const Stack = createStackNavigator();
export default function App({ navigation }) {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Welcome to dog app, and I hate react"
component={HomePage}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
function HomePage({ navigation }) {
return (
<View style={styles.container}>
<View>
<Text> {""}Welcome to dogappcoolapp app</Text>
</View>
<View style={styles.blue}>
<Login navigate={ navigation } style={styles.blue} />
</View>
</View>
)
}
The error is in this line
<Login navigate={ navigation } style={styles.blue} />
The error is
The LogIn function is in
LogIn.js
import React, { useState } from 'react';
import { StyleSheet, Text, View, Button, Script, Alert } from 'react-native';
import { NavigationContainer, StackActions, } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import 'react-native-gesture-handler';
const Stack = createStackNavigator();
export function Login({navigation}) {
if (true)
return (
navigation.navigate('WelcomePage')
);
}
function WelcomePage () {
return (
<View>
<Button title="enter" />
<Text> dog app dog woof-app{"\n\n\n\n\n\n\n\n\n"}OMG!! YOU ARE LOGGED IN! WELCOME!{"\n\n\n\n\n"}</Text>
</View>
);
}
const styles = StyleSheet.create({
blue: {
flex: 2,
backgroundColor: "dodgerblue",
alignItems: 'center',
justifyContent: 'center',
},
});
export default Login;
If I remove all of the navigation prop and tags from the function LogIn, then I can use LogIn as a componnent with <LogIn/>, but not with navigation, I tried usinng it with
navigate={ navigation }
(As it is in the code that I posted)
and I tried without it, I keep getting similar errors.
How can I use LogIn as a component with </> tags while still having navigation component in it?
The logic is correct, in child components, you can pass a navigation prop to get access to navigation, but you are passing your navigation object to a prop called navigate <Login navigate={ navigation } style={styles.blue} />, no wonder it's undefined, you should receive it as navigate in your Login component.
export function Login({navigation}) { //<-- here you have navigation where the prop name that you pass is navigate.
so it should be
export function Login({navigate}) {
...
navigate.navigate('...')
or you should rename your prop to navigation and then your navigation.navigate won't be undefined anymore.
Related
I'm trying to pass some data between two screens in my app. I'm using for this route.params from react-navigation
(here is the docs https://reactnavigation.org/docs/params/).
In the first component - home.js - I have an array with some data and FlatList component. Home.js displays data in my app correctly.
In the second component - reviewsDetails.js- I'm trying to display data /item.title/ from
home.js but I have this error: "TypeError: Cannot read properties of undefined (reading 'item')".
I am looking for a solution to this problem
Here is my code:
home.js
import React, { useState } from 'react';
import {StyleSheet, View, Text, FlatList,TouchableOpacity} from 'react-native'
function Home({navigation}) {
const [reviews, setReviews] = useState(
[
{title:"Zelda", rating:1, body:'lorem ipsum', key:1},
{title:"Cruella", rating:1, body:'lorem ipsum', key:2},
{title:"Voldemort", rating:1, body:'lorem ipsum', key:3},
]
)
return (
<View style={styles.container}>
<FlatList
data={reviews}
renderItem={({item})=>(
<TouchableOpacity onPress={()=> navigation.navigate("reviewDetails", item)}>
<Text>{item.title}</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
export default Home;
reviewDetails.js
import React from "react";
import { View, Text, Button, StyleSheet } from "react-native";
export default function ReviewDetails({ route, navigation }) {
const { item } = route.params;
return (
<View style={globalStyles.container}>
<Text>{item.title}</Text>
</View>
);
}
And here is the error
image
Codesandbox with source code: link
I will be grateful for any advice
EDIT.
Additional info :)
Here is also my navigation.js:
mport { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import React from 'react';
import about from '../screens/about'
import home from '../screens/home'
import reviewDetails from '../screens/reviewDetails'
const Tab= createBottomTabNavigator();
const AppNavigator=()=>(
<Tab.Navigator>
<Tab.Screen name="about" component={about}></Tab.Screen>
<Tab.Screen name="home" component={home}></Tab.Screen>
<Tab.Screen name="reviewDetails" component={reviewDetails}></Tab.Screen>
</Tab.Navigator>
)
export default AppNavigator;
and app.js
import React from 'react';
import { NavigationContainer, useNavigation } from "#react-navigation/native";
import AppNavigator from "./navigation/navigation.js"
export default function App(){
return(
<NavigationContainer>
<AppNavigator>
</AppNavigator> </NavigationContainer>
)
}
If its helpfull - I use version 6.0.2 of react-navigation/native and 6.0.7 of react-navigation/stack
You have to pass params like this from home screen. Pass it in an object named data (This can be named as anything you want)
onPress={() => {
navigation.navigate("reviewDetails", {
data: item,
});
}}
And then get it on the next screen like this on reviewDetails screen
const { data } = route.params;
I am trying to do user authentication using Firebase + Expo.
Currently working on the Authentication flows, but I keep getting this error that i can't seem to fix :(
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {login} from './login';
import {pageOne} from './pageOne';
import {pageTwo} from './pageTwo';
import 'react-native-gesture-handler';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import {firebase} from './fbConfig';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
export default function App() {
return (
firebase.auth().onAuthStateChanged((authenticate)=>{
if (authenticate) {
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="PageOne"
component={pageOne}
/>
<Tab.Screen
name="PageTwo"
component={pageTwo}
/>
</Tab.Navigator>
</NavigationContainer>
} else
{
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Login"
component={login}
/>
</Stack.Navigator>
</NavigationContainer>
}
})
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Its currently showing an error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
It is because the React is expecting a component to be returned from the App function but here you are returning the firebase.auth().onAuthStateChanged() which ultimately does not return any component. It(onAuthStateChanged) helps to register a listener for user's auth state change and returns a method to unsubscribe from the firebase auth listener on the component unmount(or whenever you would like to remove the auth state listener).
You can check this link for more details.
In your example, you can do it something like:
import React, {useState} from 'react';
import { StyleSheet} from 'react-native';
import { login } from './login';
import { pageOne } from './pageOne';
import { pageTwo } from './pageTwo';
import 'react-native-gesture-handler';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { firebase } from './fbConfig';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
export default function App() {
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState();
function onAuthStateChanged(user) {
setUser(user);
if (initializing) setInitializing(false);
}
useEffect(() => {
const subscriber = firebase.auth().onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
}, []);
if (initializing) return null;
if (!user) {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Login"
component={login}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="PageOne"
component={pageOne}
/>
<Tab.Screen
name="PageTwo"
component={pageTwo}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
I'm having an issue with my react-navigation-stack, I believe it could be a problem with dependencies, but I'm not sure if that's it. I am simply trying to have some text redirect to another page. If there is code that is irrelevant to the issue, such as a button, I apologize as I am trying to learn react native. The problem is being pointed at homeStack.js at the first import, "import { createStackNavigator } from '#react-navigation/stack';" , but previously I just used react-navigation-stack there, which I believe was a part of old dependencies, but it gave me a module not found error at first, which changed to what I have now when I put #react-navigation/stack instead. I was learning from a video tutorial, but the code from the tutorial was not compiling. I redownloaded react navigation multiple times and have tried some thing that did not work. I will post my code below and would really appreciate help and input as to what can help my problem. Thanks you!
Picture of error
homeStack.js
import { createStackNavigator } from '#react-navigation/stack';
//import { createAppContainer } from '#react-navigation/native';
//import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from 'react-navigation';
import Home from '../screens/home';
import ReviewDetails from '../screens/reviewDetails';
const screens = {
Home: {
screen: Home,
},
ReviewDetails: {
screen: ReviewDetails,
},
};
// home stack navigator screens
const HomeStack = createStackNavigator(screens);
export default createAppContainer(HomeStack);
home.js
import React from 'react';
import { StyleSheet, View, Text, } from 'react-native';
import { globalStyles } from '../styles/global';
export default function Home() {
return (
<View style={globalStyles.container}>
<Text style={globalStyles.titleText}>Home Screen</Text>
</View>
);
}
App.js
//import React from 'react';
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image, Button } from 'react-native';
import Buttonwithbackground from './src/Buttonwithbackground';
import { StackNavigator } from 'react-navigation';
//import Expo from 'expo';
//import Screen from 'screen2';
import { AppLoading } from 'expo';
import Navigator from './routes/homeStack';
import { render } from 'react-dom';
//
//import {Link} from 'react-router-dom';
class HomeScreen extends React.Component{
static NavigationOptions = {
title: 'Home',
};
render(){
const { navigate} = this.props.navigation;
return(
<View style={styles.container}>
<Text
onPress= { ()=> navigate('Home') }>Navigate to Profile
</Text>
</View>
);
}
}
class ProfileScreen extends React.Component{
static NavigationOptions = {
title: 'Profile',
};
render(){
const { navigate} = this.props.navigation;
return(
<View style={styles.container}>
<Text
onPress= { ()=> navigate('Profile') }>Navigate to Profile
</Text>
</View>
);
}
}
//export default function App() {
export default class App extends Component {
editUser = () => {
this.props.navigation.navigate("Screen");
// this.navigation.navigate("screen2");
// window.location.href = 'screen2';
};
editUser2 = () => {
//if the second button is clicked, it will redirect to yahoo.com
window.location.href="http://yahoo.com"
};
render(){
return (
<View style={styles.container}>
<Image
style={{width: 350, height: 200}}
source = {require('./assets/dolanduckjoker.jpg')}
/>
<Navigator />
<Buttonwithbackground text='Login' color='black' onPress={this.editUser2}/>
<p><br/></p>
<Button title='Login' color='black' onPress={this.editUser2}/>
<Button title='Login' color='black' onPress={() => navigation.navigate('screen2.js')}/>
<p><br/></p>
<Button
style={styles.cancelButton}
onPress={this.editUser}
title="Register"
color="#343434"
accessibilityLabel="Register a User."/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
},
title: {
fontSize: 32,
textAlign: 'center',
margin: 10,
},
});
The error says what's wrong -
Object(...) is not a function
createStackNavigator expects a function and you are passing an object to it as a parameter. According to docs
Your code should look like -
import { createStackNavigator } from '#react-navigation/stack';
//import { createAppContainer } from '#react-navigation/native';
//import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from 'react-navigation';
import Home from '../screens/home';
import ReviewDetails from '../screens/reviewDetails';
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="ReviewDetails" component={ReviewDetails} />
</Stack.Navigator>
);
}
export default MyStack; //you need to render this
Now in your root file, It should be something like this (excluding your additional code) -
import { createStackNavigator } from '#react-navigation/stack';
import MyStack from "yourPath";
export default function App() {
return (
<NavigationContainer>
<MyStack />
</NavigationContainer>
);
}
I have a Stack Navigator and some firebase functions in my app.js file. After implementing the Stack I am unable to use the log out button in the listScreen component because I found I cant export multiple in app.js. If I remove one export for app.js for example the Stack will work and vice versa.
App.js problem
//export navigation, container to wrap root navigator
export default createAppContainer(Switcher);
//PROBLEM cant have multiple exports, ether the Switcher or App class can export Individually
//class for app
export default class App extends Component {
Here is all the code in app.js
//Blue List is has been created by Ameer Yasin and Nick
import React, { Component } from 'react';
import { StyleSheet, Text, View, SafeAreaView } from 'react-native';
//firebase
import firebase from './servers/firebase.js';
//navigation
import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from 'react-navigation';
//components and sub component
import Login from './components/LoginPage.js';
import { SpinLoad } from './components/common/index.js';
import ListScreen from './components/ListScreen';
import AboutScreen from './components/AboutScreen';
// switch stack navigator
const Switcher = createStackNavigator(
{
//from listScreen to about screen
TaskPg: ListScreen,
AboutPg: AboutScreen
},
{
//the route of nav
initialRouteName: "TaskPg",
//header title
defaultNavigationOptions: {
title: 'BlueList'
}
}
)
//export navigation, container to wrap root navigator
export default createAppContainer(Switcher);
//PROBLEM cant have multiple exports, ether the Switcher or App class can export Individually
//class for app
export default class App extends Component {
//are u logged in set state to null
state = { loggedIn: null };
componentWillMount() {
//when logged in call this function
firebase.auth().onAuthStateChanged((user) => {
//logged in
if (user) {
this.setState({ loggedIn: true });
} //logged out
else {
this.setState({ loggedIn: false });
}
});
}
renderContent() {
//render content depending on auth status
switch (this.state.loggedIn) {
//goto list screen when logged in
case true: return (
<ListScreen />
)
//if not logged in goto log in page
case false: return <Login />;
//show loading icon by default
default: return <SpinLoad size='large' />
}
}
//render content on screen
render() {
return (
//SafeAreaView container for content
<SafeAreaView style={styles.container}>
{this.renderContent()}
</SafeAreaView>
);
}
};
//styles
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Here is the list screen where the Stack Navigator is. Again, the navigator works only if I remove the export for App in app.js therefore the sigh out wont work... I need both to work.
const ListScreen = props => {
return (
<View style={styles.container}>
{/* add task component with date picker */}
<AddItemModel />
{/* button pressed to goto About Screen */}
<TouchableOpacity onPress={() => props.navigation.navigate('AboutPg')}>
<Text style={styles.aboutBtn}>About App</Text>
</TouchableOpacity>
{/* sign out button linked to firebase log out */}
<TouchableOpacity onPress={() => firebase.auth().signOut()} >
<Text style={styles.button} >Sign Out</Text>
</TouchableOpacity>
</View>
);
}
I would really appreciate some help, I tried many methods for multiple exports as well as moving the firebase login functions in another component and still had the same issue.
UPDATE
Here is what was suggested, I tried to add the import statement with the correct path and it wont work.
import React from 'react';
import firebase from '../servers/firebase';
import { Text, StyleSheet, View, TouchableOpacity } from 'react-native';
import AddItemModel from './common/AddItemModel';
//what was suggested
import {default as AppNavigator, App} from "../App"
const ListScreen = props => {
return (
<View style={styles.container}>
{/* add task component with date picker */}
<AddItemModel />
{/* button pressed to goto About Screen */}
<TouchableOpacity onPress={() => props.navigation.navigate('AboutPg')}>
<Text style={styles.aboutBtn}>About App</Text>
</TouchableOpacity>
{/* sign out button linked to firebase log out */}
<TouchableOpacity onPress={() => firebase.auth().signOut()} >
<Text style={styles.button} >Sign Out</Text>
</TouchableOpacity>
</View>
);
}
File directory structure screenshot. The two relivent files are App.js and ListScreen.js
You can absolutely have multiple exports, you just can't have multiple default exports. Try import {default as AppNavigator, App} from "app.js".
So something like:
// app.js
export default createAppContainer(Switcher);
export class App extends Component {}
// component.js
import {default as AppNavigator, App} from "app.js"
const MyComponent = () => (
<div>
<AppNavigator/>
<App/>
</div>
);
I'm currently figuring out how to reuse a navigation which is declared in it's own class for multiple screens or to put it in another way: If my approach isn't clever react wise, is there another, better way to create a navigation that is reused on multiple screens?
Whenever I'm trying to click a button in the navigation I get an error "undefined is not an object (evaluating _this2.props.navigation.navigate). So I guess that I'm missing something about this.props in the Navigation.js.
I'm using react-navigation because it has been recommended on SO and in the react-native documentation as the way to go.
App.js
import React from 'react';
import {createStackNavigator} from 'react-navigation';
import HomeScreen from './screens/home/HomeScreen'
import ProfileScreen from './screens/profile/ProfileScreen'
import SettingsScreen from './screens/settings/SettingsScreen'
const RootStack = createStackNavigator(
{
Home: HomeScreen,
Profile: ProfileScreen,
Settings: SettingsScreen,
},
{
initialRouteName: 'Home',
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
Navigation.js - contains the planned navigation for all screens
import React from 'react';
import {StyleSheet, View, TouchableOpacity, Text} from 'react-native';
class Navigation extends React.Component {
render() {
return (
<View style={navigationStyles.footerWrapper}>
<View style={navigationStyles.footer}>
<TouchableOpacity style={navigationStyles.footerItem}
onPress={() => this.props.navigation.navigate('Home')}>
<Text style={navigationStyles.placeholderIcon}/>
</TouchableOpacity>
<TouchableOpacity style={navigationStyles.footerItem}
onPress={() => this.props.navigation.navigate('Profile')}>
<Text style={navigationStyles.placeholderIcon}/>
</TouchableOpacity>
<TouchableOpacity style={navigationStyles.footerItem}
onPress={() => this.props.navigation.navigate('Settings')}>
<Text style={navigationStyles.placeholderIcon}/>
</TouchableOpacity>
</View>
</View>
);
}
}
const navigationStyles = StyleSheet.create({
//
});
module.exports = Navigation;
HomeScreen.js - screen that should contain the navigation but displays an error when the onPress event is fired
import React from 'react';
import {StyleSheet, View, TouchableOpacity, Text} from 'react-native';
import styles from '../../common/customStyleSheet'
import Navigation from '../../components/navigation/Navigation';
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home',
header: null,
};
render() {
const {navigate} = this.props.navigation;
return (
<View style={styles.container}>
<Text style={homeScreenStyles.paddingMedium}>HomeScreen.</Text>
<Navigation/>
</View>
);
}
}
const homeScreenStyles = StyleSheet.create({
paddingMedium: {
paddingTop: 200,
},
});
module.exports = HomeScreen;
your Navigation component won't automatically inherit the navigation prop from HomeScreen because it is just a subcomponent (it is not defined in the stack navigator like the HomeScreen is). So you need to pass the navigation as a prop to the Navigation component in your HomeScreen JSX.
// HomeScreen.js
render() {
return (
<View style={styles.container}>
<Text style={homeScreenStyles.paddingMedium}>HomeScreen.</Text>
<Navigation navigation={this.props.navigation}/>
</View>
);
}