I am new to React Native and I am trying to create a simple app with tab navigation to fetch NBA data. I created a Team page with 2 buttons to fetch East or West conference team data. I tried to pass all the params from App.js to Team.js. It was meant to be fetching the data from the API when button is clicked.
However, an error occured when i tried to press the button in Team (the two buttons in Category component)
Error: Category.js:13:70 TypeError: undefined is not a function, js engine: hermes) ... a function (confOncClick) that was used in the Category.js (A component in Team page) is undefined
Error screenshot: Error
App.js
import React, {useState} from 'react';
import {View, Text, StyleSheet, FlatList, Alert} from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import {v4 as uuid} from 'uuid';
import 'react-native-get-random-values';
import Team from './pages/Team';
import Header from './components/Header';
import Home from './pages/Home';
import axios from 'axios';
const App = () => {
const [teamData, setTeamData] = useState([]);
const [confIndex, setConfIndex] = useState();
function SettingsScreen() {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Settings!</Text>
</View>
);
}
const Tab = createBottomTabNavigator();
const confOnClick = async text => {
console.log(text);
if (text === 'East') {
setConfIndex(0);
} else {
setConfIndex(1);
}
const options = {
method: 'GET',
url: 'https://api-nba-v1.p.rapidapi.com/teams',
params: {conference: text},
headers: {
'X-RapidAPI-Key': /.../,
'X-RapidAPI-Host': 'api-nba-v1.p.rapidapi.com',
},
};
await axios
.request(options)
.then(function (response) {
console.log(response.data.response);
console.log(typeof response.data.response);
setTeamData(response.data.response);
})
.catch(function (error) {
console.error(error);
});
};
return (
<View style={styles.container}>
<Header />
<NavigationContainer>
<Tab.Navigator screenOptions={{headerShown: false}}>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen
name="Team"
component={Team}
options={{confOnClick: confOnClick, teamData: teamData}}
/>
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default App;
Team.js
import React from 'react';
import {View, StyleSheet, FlatList, Alert} from 'react-native';
import ListItem from '../components/ListItem';
import Category from '../components/Category';
function Team({confOnClick, teamData}) {
return (
<View>
<Category confOnClick={confOnClick} />
<FlatList
data={teamData}
renderItem={({item}) => <ListItem item={item} />}
/>
</View>
);
}
export default Team;
Category.js
import React from 'react';
import {View, StyleSheet, FlatList, Alert} from 'react-native';
import ListItem from '../components/ListItem';
import Category from '../components/Category';
function Team({confOnClick, teamData}) {
return (
<View>
<Category confOnClick={confOnClick} />
<FlatList
data={teamData}
renderItem={({item}) => <ListItem item={item} />}
/>
</View>
);
}
export default Team;
the confOnClick passed to Category seems to be undefined but I am not sure what is the issue. Could be because of the way I pass from app.js?
<Tab.Screen
name="Team"
component={Team}
options={{confOnClick: confOnClick, teamData: teamData}}
/>
hope I made it clear and please let me know if I can provide more info
I have tried passing the params in different ways in Tab.Screen navigator with initialParams, params, options and also change the component to children but somehow i didn't get it right.
you can use initialParams in Tab.Screen like
<Tab.Screen
name="Team"
component={Team}
initialParams={{confOnClick: confOnClick, teamData: teamData}}
/>
Team.js
function Team({route}) {
const {confOnClick, teamData} = route.params;
...
}
Related
I am a beginner of react native. After reading the reactive navigation basics, I'm trying to build my personal app. However, I have a difficulty of navigation to another components.
I only have the following components:
app.js
import { StatusBar } from 'expo-status-bar';
import { Button, StyleSheet, Text, View, TextInput, Image } from 'react-native';
import * as React from 'react';
import { NavigationContainer, StackActions } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import Dict from './dict';
const Stack = createNativeStackNavigator();
const HomeScreen = ({ navigation }) => {
return (
<View style={{ height: '100%' }}>
<View style={{
height: '15%', backgroundColor: 'powderblue', flexDirection:'row'
}} />
<Text>This is a test</Text>
<Button title="Go to Dictionary" onPress={() => navigation.navigate('Dict')}/>
</View>
);
};
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerShown: false }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
dict.js
import React from 'react';
import { Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
function Dict () {
return (
<Text>Hello, I am your cat!</Text>
);
}
export default Dict;
When I click the button Go to Dictionary, I've received the following error message:
The action 'NAVIGATE' with payload {"name":"Dict"} was not handled by any navigator.
Do you have a screen named 'Dict'?
What I want to achieve is jump to the dictionary page via a button click. Thank you for your help!
You have to create your Dict screen in your Navigator, try this:
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Dict"
component={Dict}
options={{ headerShown: false }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
I'm new to React Native and I'm trying to perform a simple stack navigation. I got it to work in another part of the application (User authentication step). My code then feeds onto another stack once the user is logged in. This stack navigator has a tab navigator nested in which may be causing the problem?
Either way I can't perform a push, from my profile screen to the edit profile screen. Code is below.
import React from 'react'
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from './screens/LoginScreen';
import HomeScreen from './screens/HomeScreen';
import SignupScreen from './screens/SignupScreen';
import ProfileScreen from './screens/ProfileScreen';
import EditProfileScreen from './screens/EditProfileScreen';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/Ionicons';
const Stack = createNativeStackNavigator()
const screenOptions = {
headerShown: false
}
export const SignedOutStack = () =\> (
\<NavigationContainer\>
\<Stack.Navigator
initialRouteName="LoginScreen"
screenOptions={screenOptions}
\\>
\<Stack.Screen
name="Login"
component={LoginScreen}
/\>
\<Stack.Screen
name='SignupScreen'
component={SignupScreen}
/\>
\</Stack.Navigator\>
\</NavigationContainer\>
)
const Tab = createBottomTabNavigator();
export const SignedInStack = () =\> (
\<NavigationContainer\>
\<Tab.Navigator
screenOptions={screenOptions}\>
\<Tab.Screen name="Home" component={HomeScreen} /\>
\<Tab.Screen name="Profile" component={ProfileScreen} /\>
\</Tab.Navigator\>
\</NavigationContainer\>
)
export const ProfileStack = () =\> (
\<NavigationContainer\>
\<Stack.Navigator\>
\<Stack.Screen name="SignedInStack" component={SignedInStack} /\>
\<Stack.Screen name="EditProfile" component={EditProfileScreen} /\>
\</Stack.Navigator\>
\</NavigationContainer\>
)
Where I'm trying to implement the push
import { View, Text, SafeAreaView, ScrollView, StyleSheet, Image, TouchableOpacity, StatusBar, Button } from 'react-native'
import React, {useState, useEffect, useContext} from 'react';
import EditProfileScreen from './EditProfileScreen';
const ProfileScreen = ({navigation}) =\> (
// const {user, logout} = useContext(AuthContext)
<SafeAreaView style={styles.wrapper}>
<ScrollView
style={styles.container}
contentContainerStyle={{justifyContent: 'center', alignItems: 'center'}}
showsVerticalScrollIndicator = {false}
>
<Image style={styles.userImg} source={{uri: 'https://www.dmarge.com/wp-content/uploads/2021/01/dwayne-the-rock-.jpg'}} />
<Text style={styles.userName}>Person</Text>
<View style={styles.userBtnWrapper}>
<Button title="Edit Profile" onPress = {() => {
navigation.push("EditProfile")
}}/>
</View>
</ScrollView>
</SafeAreaView>
)
export default ProfileScreen
You may need to declare a stackAction before actually use navigation.
import { StackActions } from '#react-navigation/native';
....
const pushAction = StackActions.push('Profile', { user: 'Wojtek' });
navigation.dispatch(pushAction);
I've been trying to map an array of items which is passed from my Home Component to another but I'm always getting the error restaurants.map is Undefined. I fetch the data from Yelp and store it in a State(Home Component), then I pass this to another component, retrieve it through props and try to map through it(RestaurantItems Component). Please help. I have attached the code of my two components..
Home Component
import { View, Text, StatusBar, ScrollView } from "react-native";
import React, { useEffect, useState } from "react";
import HeaderTabs from "../components/HeaderTabs";
import SearchBar from "../components/SearchBar";
import Categories from "../components/Categories";
import RestaurantItems from "../components/RestaurantItems";
const YELP_API_KEY =
"6NE-noDkyFUDKVVo2B8POXtwsAIBEe7QTnZEwMpvNE-5asFQ1q0_jh7iJ5KqprLD3sVt2htFrZbe4V2rHbdXgUcqkMSquQADGcGOTh_ANZ1DRT_tnFKZBHT4Hh0eYn";
export default function Home() {
const [restaurantData, setRestaurantData] = useState();
const getRestaurantFromYelp = async () => {
const response = await fetch(
`https://api.yelp.com/v3/businesses/search?term=restaurants&location=san+francisco`,
{
method: "GET",
headers: {
Authorization: `Bearer ${YELP_API_KEY}`,
},
}
);
const data = await response.json();
setRestaurantData(data);
};
useEffect(() => {
getRestaurantFromYelp();
}, []);
return (
<View style={{ backgroundColor: "#eee", flex: 1 }}>
<StatusBar barStyle="dark-content" backgroundColor="#eee" />
<View style={{ backgroundColor: "#fff", padding: 15 }}>
<HeaderTabs />
<SearchBar />
</View>
<ScrollView showsVerticalScrollIndicator={false}>
<Categories />
<RestaurantItems restaurants={restaurantData} />
</ScrollView>
</View>
);
}
RestaurantItems Component
import React from "react";
import { MaterialCommunityIcons } from "react-native-vector-icons";
export default function RestaurantItems({ restaurants }) {
console.log(restaurants);
return (
<View>
{restaurants.map((single_data) => (
<Text>{single_data.name}</Text>
))}
</View>
);
}
This has been asked so many times!
Your state is not initialized, so at first render, when your datas are not loaded yet, you have an error. So initiliaze your state with an empty array
const [restaurantData, setRestaurantData] = useState([]);
this url: https://streamable.com/no6anz, is a video showcasing my app's navigation, I want the ability to navigate from a Drawer screen to a SPECIFIC Tab Screen in my Tab Navigation.
For example: If i pressed let's say the 'Messages' drawer item, i would like to navigate to the My Account tab screen in the tab navigator.
So far my implementation has not been the best:
import React, {Fragment, Component} from 'react';
import {View, StyleSheet} from 'react-native';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import {createDrawerNavigator} from '#react-navigation/drawer';
import {createStackNavigator} from '#react-navigation/stack';
import Home from '../Views/HomeView';
import Cart from '../Views/CartView';
import MyAccount from '../Views/MyAccountView';
import Sidebar from '../Views/Sidebar';
import HomeIconColor from './CustomSVGIcons/HomeIconColor';
import CartIconColor from './CustomSVGIcons/CartIconColor';
import PersonIconColor from './CustomSVGIcons/PersonIconColor';
import MenuIconColor from './CustomSVGIcons/MenuIconColor';
import Contact from '../Views/screens/Contact';
import Messages from '../Views/screens/Messages';
import {
NavigationContainer,
DrawerActions,
useNavigation,
} from '#react-navigation/native';
import {BottomTabBar} from '#react-navigation/bottom-tabs';
import Login from '../Views/screens/Login';
import {create} from 'react-test-renderer';
import {createSwitchNavigator} from '#react-navigation/compat';
const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();
const Stack = createStackNavigator();
const Switch = createSwitchNavigator();
class Navigator extends Component {
constructor() {
super();
this.state = {};
}
render() {
return (
<NavigationContainer>
<MyDrawerNavigation />
</NavigationContainer>
);
}
}
const MyTabs = (props) => {
const navigation = useNavigation();
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
screenOptions={({route}) => ({
tabBarButton: ['Contact', 'Route2ToExclude'].includes(route.name)
? () => {
return null;
}
: undefined,
})}>
<Tab.Screen
name="Home"
component={Home}
/>
<Tab.Screen
name="MyAccount"
component={MyAccount}
/>
<Tab.Screen
name="Cart"
component={Cart}
/>
<Tab.Screen
name="Sidebar"
component={Sidebar}
/>
<Tab.Screen name="Contact" component={Contact} />
</Tab.Navigator>
);
};
const MyDrawerNavigation = () => {
return (
<Drawer.Navigator drawerPosition={'right'}>
<Drawer.Screen
name="Home"
component={MyTabs}
initialParams={{screen: 'Home'}}
/>
<Drawer.Screen
name="My Account"
component={MyTabs}
initialParams={{screen: 'MyAccount'}}
/>
</Drawer.Navigator>
);
};
const styles = StyleSheet.create({
tabNav: {
backgroundColor: '#000000',
},
});
export default Navigator;
You can pass the screen as the initial param and it would redirect to the correct tab.
<Drawer.Screen name="MyTabs" component={'Tab Component Name'} initialParams={{screen:'MyAccount'}}/>
SOLVED!:
After extensively further researching; I have created my own custom Drawer, with the onPress handler.
The onPress handler initiates the action for navigation to the specified screens.
NOTE: you will have to pass in the navigation ref to the top-level navigation container. You will also have to pass this same reference into your custom drawer module (or any module in that concern)
here's my custom drawer component:
import React, {Component} from 'react';
import {Text, View, Button, StyleSheet} from 'react-native';
import {useNavigation, CommonActions} from '#react-navigation/native';
import * as rootNavigator from '../components/rootNavigator';
const CustomDrawer = () => {
return (
<View style={styles.container}>
<Button
title="My Account"
onPress={() => rootNavigator.navigate('MyAccount')}
/>
<Button
title="Contact"
onPress={() => rootNavigator.navigate('Contact')}
/>
<Button title="Cart" onPress={() => rootNavigator.navigate('Cart')} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 30,
},
});
export default CustomDrawer;
here's the code where I return the DrawerNavBar:
const MyDrawerNavigation = () => {
return (
<NavigationContainer ref={navigationRef}>
<Drawer.Navigator
drawerPosition={'right'}
drawerContent={(props) => <CustomDrawer {...props} />}>
<Drawer.Screen name="Tabs" component={MyTabs} />
<Drawer.Screen name="Contact" component={Contact} />
</Drawer.Navigator>
</NavigationContainer>
);
};
I am coding an application that will have more than one page. I use '#react-navigation/native' and '#react-navigation/stack' to navigate between these pages. It works correctly when I write a function name in Stack.Screen's component. But I did not understand how the class call was made.
Api.js
import * as React from 'react';
import { StyleSheet, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
const Stack = createStackNavigator();
export default function App(props) {
// Load any resources or data that we need prior to rendering the app
return (
<View style={styles.container}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Homa Page" component={HomeScreen}/>
</Stack.Navigator>
</NavigationContainer>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
screens/HomeScreen.js
import * as React from 'react';
import { Text, View } from 'react-native';
import { getSummary } from '../data/getSummary.js';
export class HomeScreen extends Component {
constructor(props){
super(props);
this.state ={
isLoading: true,
}
this.getSummary = getSummary.bind(this)
}
componentDidMount(){
this.getSummary;
}
render(){
return (
<View style={{flex: 1, paddingTop:20}}>
<FlatList
data={ this.state.data}
keyExtractor={(item) => item.CountryCode}
renderItem={({ item }) => (
<View>
<Text>Country:{item.CountryCode}</Text>>
</View>
)}
/>
</View>
);
}
}