How to display a nested screen within another nested screen? - javascript

My app is currently set up as follows, and I want to show the Hub screen when the user presses on Study:
App.js:
import React, { Component } from 'react';
...
import { View } from 'react-native'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './redux/reducers'
import thunk from 'redux-thunk'
const store = createStore(rootReducer, applyMiddleware(thunk))
...
export class App extends Component {
...
render() {
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator initialRouteName="Main">
<Stack.Screen name="Main" component={MainScreen} options={{ headerShown: false }}/>
</Stack.Navigator>
</NavigationContainer>
</Provider>
)
}
}
export default App
Main:
import React, { Component } from 'react'
import { View } from 'react-native'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { fetchUser, clearData } from '../redux/actions/index'
import ProfileScreen from './main/Profile'
import HomeScreen from './main/Home'
import StudyScreen from './main/Study'
import TestScreen from './main/Test'
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs'
const Tab = createMaterialBottomTabNavigator();
// https://reactnavigation.org/docs/bottom-tab-navigator/
export class Main extends Component {
componentDidMount() {
this.props.clearData();
this.props.fetchUser();
}
render() {
...
return (
<Tab.Navigator initialRouteName="Home" activeColor="#f0edf6" barStyle={{ backgroundColor: '#694fad' }} shifting='true'>
<Tab.Screen name="Home" component={HomeScreen}
options={{
tabBarLabel: 'Home', tabBarColor: '#FF6347', tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="home" color={color} size={26}/>
),
}} />
<Tab.Screen name="Study" component={StudyScreen}
options={{
tabBarLabel: 'Study', tabBarColor: '#694FAD', tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="school" color={color} size={26}/>
),
}} />
<Tab.Screen name="Profile" component={ProfileScreen}
options={{
tabBarLabel: 'Profile', tabBarColor: '#1F65FF', tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="account" color={color} size={26}/>
),
}} />
<Tab.Screen name="Test" component={TestScreen}
options={{
tabBarLabel: 'Test', tabBarColor: '#3490AA', tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="history" color={color} size={26}/>
),
}} />
</Tab.Navigator>
)
}
}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser
})
const mapDispatchProps = (dispatch) => bindActionCreators({fetchUser, clearData}, dispatch)
export default connect(mapStateToProps, mapDispatchProps)(Main);
Study:
import React, { Component } from 'react'
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const Stack = createStackNavigator();
import HubScreen from './studyWebviews/Hub'
export class Study extends Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Hub">
<Stack.Screen name="Hub" component={HubScreen} options={{ headerShown: false }}/>
</Stack.Navigator>
</NavigationContainer>
);
}
}
Hub:
import React from 'react'
import { Text } from 'react-native'
export default function Hub() {
return (
<Text>Hub</Text>
)
}
Trying to load my app gives this error:
×
Error: Couldn't find a 'component', 'getComponent' or 'children' prop for the screen 'Study'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing.
How do I fix this? Please let me know if I need to clarify anything or make any changes for the sake of readability, thank you.

I believe you are importing the components incorrectly, as the message suggests: "...or mixed up default import and named import when importing."
Try export default class Study...

Related

How to remove default styles from react-navigation?

I was making an app with react native and react-navigation. So what I did was I made a Login screen. Then I used react-navigation to create a native stack navigator and linked it to my Login screen. I successfully rendered the Login Screen but there seems to be some sort of default styling on the stack navigator(?). How do I remove or overwrite those styles so that the original styling of my screens come back? Images and Code below.
This is the stack navigator
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from '../screens/LoginScreen';
import SignUpScreen from '../screens/SignUpScreen';
const Stack = createNativeStackNavigator();
const AuthStack = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login" screenOptions={{ header: () => null }}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Signup" component={SignUpScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default AuthStack;
When I only render the LoginScreen it looks like this
When I use AuthStack it looks like this
Should have read the docs nicely it was there in the NativeStackNavigator options.
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from '../screens/LoginScreen';
import SignUpScreen from '../screens/SignUpScreen';
const Stack = createNativeStackNavigator();
const AuthStack = () => {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Login"
screenOptions={{
headerShown: false,
header: () => null,
contentStyle: { backgroundColor: 'white' },
}}
>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Signup" component={SignUpScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default AuthStack;
To do your own style do this
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer, DefaultTheme } from "#reactnavigation/native";
const MyTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: Color.red,
},
};
Then do this
<NavigationContainer theme={MyTheme}>
<Stack.Navigator initialRouteName="startscreen" headerMode="none" >
</Stack.Navigator>
</NavigationContainer>
More information here (https://reactnavigation.org/docs/themes/)

Bottom Navigation TabBarIcon render method is wrong?

I tried to create bottom Navigation,
Following this link : https://reactnavigation.org/docs/tab-based-navigation/#customizing-the-appearance
and here is my code :
import React from "react";
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import Ionicons from "react-native-vector-icons";
// Screens
import Home from "./Home";
import Settings from "./Settings";
const Tab = createBottomTabNavigator();
function MainContainer({ navigation }) {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === "Home") {
iconName = focused
? "ios-information-circle"
: "ios-information-circle-outline";
} else if (route.name === "Settings") {
iconName = focused ? "ios-list-box" : "ios-list";
}
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: "tomato",
tabBarInactiveTintColor: "gray",
})}
>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Settings" component={Settings} />
</Tab.Navigator>
);
}
export default MainContainer;
But I got this error :
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `TabBarIcon`.
I found out the mistake,
on the
import Ionicons from "react-native-vector-icons"; it should have been import Ionicons from "react-native-vector-icons/Ionicons"
Solutions:
You need wrap your <Tab.Navigator> into root navigation container
Example:
You need to first install dependency of #react-navigation/native
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const MainContainer = () => {
return (
<NavigationContainer>
<Tab.Navigator />
<Stack.Navigator />
...other your navigation
</NavigationContainer>
);
};
You can checkout react-navigaiton https://reactnavigation.org/docs/getting-started/
Just wrape your <Tab.Navigator> in <NavigationContainer>
import React from "react";
import { NavigationContainer } from '#react-navigation/native'
//Reset Imports
const Routes = () => {
return (
<NavigationContainer>
<Tab.Navigator>
</Tab.Navigator>
</NavigationContainer>
)
}
export default Routes

React-Native nested navigation not working in expo

I'm using the React Native Stack Navigation to configure the header in my app and then nest a Drawer Navigation inside of it.
In the android emulator, everything is working fine. But whenever I try to open the app using expo, there's nothing more then a white blank screen. Developer tools aren't logging any errors, Expo itself doesn't give me an error nor does the terminal.
I tried replacing the whole navigation with just a <Text> component and in this case Expo shows the text. But I can't seem to find what I'm doing wrong. Some help would be much appreciated since I'm just learning React Native.
This is my code:
index.tsx
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
App.tsx
import React, {Component} from 'react';
import {NavigationContainer} from '#react-navigation/native';
import RootStack from './src/__plugins/navigation';
export default class App extends Component {
render() {
return (
<NavigationContainer>
<RootStack />
</NavigationContainer>
);
}
}
navigation/index.tsx
import {createStackNavigator} from '#react-navigation/stack';
import SignOutModalScreen from '../../_view/SignOutModalScreen';
import {TouchableOpacity} from 'react-native-gesture-handler';
import {FontAwesomeIcon} from '#fortawesome/react-native-fontawesome';
import {faBars, faSignOutAlt, faTimes} from '#fortawesome/free-solid-svg-icons';
import {StyleSheet} from 'react-native';
import StartDrawer from './StartDrawer';
import {DrawerActions} from '#react-navigation/native';
export enum RoutingStack {
START = 'start',
GAME = 'main'
}
export enum RoutingDrawer {
START = 'start',
GAME = 'game'
}
export enum RoutingIdentifier {
JOIN_SCREEN = 'join',
GAME_SCREEN = 'game',
HELP_SCREEN = 'help',
SIGNOUT_SCREEN = 'sign_out'
}
const Stack = createStackNavigator();
const RootStack = () => {
return (
<Stack.Navigator
mode="modal"
screenOptions={({ navigation }) => ({
headerStyle: {
backgroundColor: '#80cbc4',
},
headerLeft: () => {
return (
<TouchableOpacity onPress={() => navigation.dispatch(DrawerActions.toggleDrawer)}>
<FontAwesomeIcon icon={faBars} style={styles.menuIcon} />
</TouchableOpacity>
);
},
headerRight: () => {
return (
<TouchableOpacity onPress={() => navigation.navigate(RoutingIdentifier.SIGNOUT_SCREEN)}>
<FontAwesomeIcon icon={faSignOutAlt} style={styles.signOutIcon} />
</TouchableOpacity>
);
},
})}
>
<Stack.Screen
name={RoutingDrawer.START}
component={StartDrawer}
options={{ title: '' }}
/>
<Stack.Screen
name={RoutingIdentifier.SIGNOUT_SCREEN}
component={SignOutModalScreen}
options={({ navigation }) => ({
headerTitle: '',
headerStyle: {
elevation: 0,
backgroundColor: '#F5FCFF',
},
headerLeft: () => {
return (
<TouchableOpacity onPress={() => navigation.navigate(RoutingDrawer.START)}>
<FontAwesomeIcon icon={faTimes} style={styles.closeIcon} />
</TouchableOpacity>
);
},
})}
/>
</Stack.Navigator>
);
};
const styles = StyleSheet.create({
closeIcon: {
marginStart: 10,
color: 'black',
},
menuIcon: {
marginStart: 10,
color: 'white',
},
signOutIcon: {
marginEnd: 10,
color: 'white',
},
});
export default RootStack;
And the StartDrawer.tsx
import {createDrawerNavigator} from '#react-navigation/drawer';
import {RoutingIdentifier} from './index';
import JoinPage from '../../join/_view/JoinScreen';
import {FontAwesomeIcon} from '#fortawesome/react-native-fontawesome';
import {faQuestionCircle, faSignInAlt} from '#fortawesome/free-solid-svg-icons';
import {StyleSheet} from 'react-native';
import {trans} from '../i18n';
const Drawer = createDrawerNavigator();
const StartDrawer: FC = () => {
return (
<Drawer.Navigator drawerType="slide" hideStatusBar={false}>
<Drawer.Screen
name={RoutingIdentifier.JOIN_SCREEN}
component={JoinPage}
options={{
drawerLabel: trans.getString('MENU_START_GAME'),
drawerIcon: () => (
<FontAwesomeIcon icon={faSignInAlt} style={styles.icon} />
),
}}
/>
<Drawer.Screen
name={RoutingIdentifier.HELP_SCREEN}
component={() => null}
options={{
drawerLabel: trans.getString('MENU_HELP'),
drawerIcon: () => (
<FontAwesomeIcon icon={faQuestionCircle} style={styles.icon} />
),
}}
/>
</Drawer.Navigator>
);
};
const styles = StyleSheet.create({
icon: {
marginEnd: -20,
marginStart: 10,
},
});
export default StartDrawer;
I am not sure but have you tried the opposite? stack inside drawer instead of drawer inside of stack?
I have had good results with this. Perhaps my root navigator may help you. Try using it as a template (I have written this code some time ago so I don't remember specifics but it works well) :
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { fetchPaymentMethods } from 'reducers/paymentMethods';
import { fetchNextPayments } from 'reducers/nextPayments';
import Home from 'pages/home';
import Orders from 'pages/orders';
import Settings from 'pages/settings';
import Scanner from 'pages/scanner';
import Support from 'pages/support';
import SideDrawer from 'components/SideDrawer';
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
const StackNavigator = () => (
<Stack.Navigator headerMode="none">
<Stack.Screen name="Home" component={Home} />
<Drawer.Screen name="Orders" component={Orders} />
<Drawer.Screen name="Settings" component={Settings} />
<Drawer.Screen name="Scanner" component={Scanner} />
<Drawer.Screen name="Support" component={Support} />
</Stack.Navigator>
);
const RootNavigator = props => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchNextPayments());
dispatch(fetchPaymentMethods());
}, []);
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName="Home"
screenOptions={{ gestureEnabled: true }}
drawerContent={props => <SideDrawer {...props} />}
>
<Drawer.Screen name="Root" component={StackNavigator} />
<Drawer.Screen name="Home" component={Home} />
<Drawer.Screen name="Orders" component={Orders} />
<Drawer.Screen name="Settings" component={Settings} />
<Drawer.Screen name="Scanner" component={Scanner} />
<Drawer.Screen name="Support" component={Support} />
</Drawer.Navigator>
</NavigationContainer>
);
};
export default RootNavigator;

React Native: Passing navigation route as props into dynamically rendered component

I am building an app in which several of the screens have dynamically rendered cards that are mapped to an array of objects called ENTRIES. Each one of these cards can be pressed to navigate to a corresponding screen, however, I cannot seem to get the navigation to work.
I am passing is the screen value from ENTRIES as props from the Settings.js screen into the ClickableCard component, which then gets passed into the TouchableOpacity onClick as this.props.navigation.navigate(screen).
However I keep getting the following error TypeError: undefined is not an object (evaluating '_this3.props.navigation.navigate')
Here is an example of the code below:
App.js File
import React from 'react;
import {createMaterialBottomTabNavigator} from '#react-navigation/material-bottom-tabs';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import Home from './src/screens/Home';
import SettingsScreen from './src/screens/SettingsScreen';
import PrivacyScreen from './src/screens/PrivacyScreen';
import NotificationsScreen from './src/screens/NotificationsScreen';
import SoundsScreen from './src/screens/SoundsScreen';
import ThemeScreen from './src/screens/ThemeScreen';
const PrivacyStack = createStackNavigator();
const SettingsStack = createStackNavigator();
const AuthStack = createStackNavigator();
const MainStack = createStackNavigator();
const Tabs = createMaterialBottomTabNavigator();
const TabNavigator = () => {
return (
<Tabs.Navigator
initialRouteName="Home"
<Tabs.Screen
name="Home"
component={HomeStack}
/>
Tabs.Screen
name="Settings"
component={SettingsStack}
children={this.SettingsStack}
</Tabs.Navigator>
)
}
const AuthStack = () => (
<AuthStack.Navigator>
<AuthStack.Screen
name="Auth"
component={Auth}
/>
</AuthStack.Navigator>
);
const SettingsStackScreen = () => (
<SettingsStack.Navigator>
<SettingsStack.Screen
name="Settings"
component={Settings}
/>
<SettingsStack.Screen
name="Privacy"
component={PrivacyStack}
/>
<SettingsStack.Screen
name="Theme"
component={ThemeScreen}
/>
<SettingsStack.Screen
name="Notifications"
component={NotificationsScreen}
/>
<SettingsStack.Screen
name="Sound"
component={SoundsScreen}
/>
</SettingsStack.Navigator>
);
const PrivacyStack = () => (
<PrivacyStack.Navigator>
<PrivacyStack.Screen
name="Privacy"
component={PrivacyScreen}
/>
<PrivacyStack.Screen
name="Notifications"
component={NotificationsScreen}
/>
</PrivacyStack.Navigator>
);
const App = () => {
return (
<NavigationContainer ref={navigationRef}>
<MainStack.Navigator>
<MainStack.Screen name="Tabs" component={TabNavigator} />
<MainStack.Screen
name="Auth"
component={AuthStack}
options={{gestureEnabled: false}}
/>
</MainStack.Navigator>
</NavigationContainer>
)
}
Settings.js File
import React, {Component} from 'react';
import {TouchableOpacity, ScrollView} from 'react-native;
import ClickableCard from './ClickableCard'
export default class Settings extends Component {
render(screen, index) {
return (
<ScrollView>
{ENTRIES.map((entry, index) => (
<ClickableCard screen={entry.screen} key={entry.index}/>
))}
</ScrollView>
)
}
export default Settings
ClickableCard.js Component
import React, {Component} from 'react';
import {TouchableOpacity, ScrollView} from 'react-native;
export default class ClickableCard extends Component {
constructor(props) {
super(props);
}
render() {
const {
screen,
key
} = this.props
return (
<TouchableOpacity
key={key}
onPress={() => this.props.navigation.navigate(screen)}>
</TouchableOpacity>
)
}
}
entries.js File
import React from 'react';
export const ENTRIES = [
{
name: "Theme",
screen: "ThemeScreen",
},
{
name: "Sounds",
screen: "SoundsScreen",
},
{
name: "Notifications",
screen: "NotificationsScreen",
},
]
You are trying to access navigation outside the navigation stack.
If you are using a functional component you can go with the useNavigation hook but as this is a class based component you will have to send the navigation as a prop or you can do the below as suggested in the documentation
import { useNavigation } from '#react-navigation/native';
class ClickableCard extends Component {
constructor(props) {
super(props);
}
render() {
const { screen, key } = this.props;
return (
<TouchableOpacity
key={key}
onPress={() =>
this.props.navigation.navigate(screen)
}></TouchableOpacity>
);
}
}
const ClickableCardWithNavigation= (props) {
const navigation = useNavigation();
return <ClickableCard {...props} navigation={navigation} />;
}
export default connect(ClickableCardWithNavigation)(TodoList)

How to implement details screen that dynamically displays detail of pokemon when a user presses on a picture in react native?

So I have built a pokemon application that would display pokemon with their images and details. On the homescreen, I have it so that the first 20 pokemon are shown as a preview. Then if the user wants to learn more about the pokemon, they can press on the pokemon and it would bring the user to the "PokeDetails" screen. However, when I press on the pokemon, it brings me to the "PokeDetails" page but the page is blank. Except for the text that I explicitly render from the "PokeDetails" screen. I have the code for both screens below. Any help is appreciated. Thank you
//Home.js
import React, { useState } from "react";
import { View, Text , Button, FlatList, ActivityIndicator, TouchableOpacity, Image } from "react-native";
import { GlobalStyles } from "../styles/GlobalStyles";
import PokeDetails from "./PokeDetails";
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: [],
}
}
componentDidMount() {
fetch(`https://pokeapi.co/api/v2/pokemon/?limit=20`)
.then((res)=> res.json())
.then((response)=> {
this.setState({
isLoading: false,
dataSource: response.results,
})
console.log("RESPONSE",response)
console.log("RESPONSE.RESSSULTS",response.results)
})
}
render() {
const showIndicator = this.state.isLoading == true ? <ActivityIndicator size="large" color="#0000ff" /> : null;
return(
<View style={GlobalStyles.container}>
<View style={GlobalStyles.activityIndicator}>{showIndicator}</View>
<FlatList
numColumns={1}
data={this.state.dataSource}
renderItem={({item})=>
<TouchableOpacity onPress={()=> this.props.navigation.navigate("PokeDetails", {item} )}>
<PokeDetails imageUrl={`https://projectpokemon.org/images/normal-sprite/${item.name}.gif`} name={item.name} item={item} />
</TouchableOpacity>
}/>
<Button onPress={()=> this.props.navigation.navigate("About")} title="Go to about"/>
</View>
)
}
}
export default Home;
// PokeDetails.js
import React from "react";
import { View, Text , Image, Button} from "react-native";
import {GlobalStyles} from "../styles/GlobalStyles";
import { TouchableOpacity } from "react-native-gesture-handler";
import { useRoute } from '#react-navigation/native';
const PokeDetails =({name, imageUrl, detail, route})=> {
return(
<View style={GlobalStyles.container}>
<Text>This text is written expilictly</Text>
<Text>{route.params.item}</Text>
</View>
)
}
export default PokeDetails;
// Root.js
import React from "react"
import { createStackNavigator } from "#react-navigation/stack";
import Home from "../screens/Home";
import PokeDetails from "../screens/PokeDetails";
import { NavigationContainer } from '#react-navigation/native';
const Root =() => {
const Stack = createStackNavigator();
return(
<Stack.Navigator>
<Stack.Screen name="Home" component={Home}/>
<Stack.Screen name="PokeDetails" component={PokeDetails}/>
</Stack.Navigator>
)
}
export default Root;
// App.js
import 'react-native-gesture-handler';
import React from 'react';
import { View , StyleSheet } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createDrawerNavigator } from "#react-navigation/drawer";
import About from "./screens/About";
import Root from "./Route/Root";
import PokeDetails from "./screens/PokeDetails"
const App =()=> {
const Drawer = createDrawerNavigator();
return(
<View style={styles.container}>
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={Root}/>
<Drawer.Screen name="About" component={About}/>
</Drawer.Navigator>
</NavigationContainer>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1
}
})
export default App;
I don't know if i understood your question. You already pass an item props to your PokeDetails component from your Homescreen, but there is no item in the props your receive in it. just add it and i guess you will access to your data and will be able to display it:
const PokeDetails =({name, imageUrl, detail, item})=> {
return(
<View style={GlobalStyles.container}>
<Image source={{uri: imageUrl}} style={{height: 50, width: 50}}/>
<Text style={GlobalStyles.pokeText}>{name}</Text>
<Text>This text is explicitly written</Text>
// add your stuff here...
<Text>{item.something}</Text>
</View>
)
}
By the way, since you already have the item prop, you can remove the name which already comes from item :)

Categories

Resources