Passing props and navigation in react navigation - react-native

I have a parent functional component Layout which needs props to have access to it's children. However, as I am using navigation 5.x, I also need to pass navigation but what I have tried hasn't worked yet. Here is my work
import { Container, Header, Content, Left, Right } from 'native-base';
import React from 'react';
import { View } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import { useNavigation } from '#react-navigation/native';
const Layout = (props, navigation) => {
return (
<Container >
<Header style={{ alignItems: 'center', backgroundColor: '#3a455c', height: 50 }}>
<Left style={{ flexDirection: 'row' }}>
<MaterialIcon style={{ color: 'white', marginRight: 10 }} onPress={ () => console.log(navigation) } size={30} color="black" name= 'menu' />
</Left>
<Icon onPress={ () => navigation.navigate('Index') } size={20} style={{marginTop: 4, marginLeft: 130}} color="white" name= 'home' />
<Right style={{ flexDirection: 'row' }}>
<Icon onPress={ () => navigation.navigate('Index') } size={20} style={{marginTop: 4}} color="green" name= 'user' />
</Right>
</Header>
<Content >
<View>
{ props.children }
</View>
</Content>
</Container>
);
}
export default Layout;
Layout component is being rendered in Newsletter component
function Newsletter() {
const navigation = useNavigation();
return (
<Layout >
<ScrollView style={{ marginTop: 40, marginLeft: 12 }}>
<Text style={{fontSize: 24, fontFamily: 'sans-serif-medium', textAlign: 'center', fontWeight: 'bold'}} > Directed Distraction </Text>
<View>
<Text style={{ marginTop: 12, fontSize: 18, textAlign: 'left', fontStyle: 'italic' }}>
Elon Reeve Musk FRS (/ˈiːlɒn/; born June 28, 1971) is an engineer,
industrial designer, technology entrepreneur and philanthropist.
He is a citizen of South Africa, Canada, and the United States.
He is based in the United States, where he immigrated to at age 20 and
has resided since. He is the founder, CEO and chief engineer/designer
of SpaceX; early investor, CEO and product architect of Tesla,
Inc.; founder of The Boring Company; co-founder of Neuralink; and
co-founder and initial co-chairman of OpenAI. He was elected a Fellow of
the Royal Society (FRS) in 2018. In December 2016, he was ranked 21st
on the Forbes list of The World's Most Powerful People, and was ranked
joint-first on the Forbes list of the Most Innovative Leaders of 2019.
A self-made billionaire, as of July 2020 his net worth was estimated at $44.9
billion and he is listed by Forbes as the 22nd-richest person in the world.
He is the longest tenured CEO of any automotive manufacturer globally.
</Text>
<Button title="a" onPress={ () => console.log(navigation) }></Button>
</View>
</ScrollView>
</Layout>
);
}
navigation is printed just as an object{}. Help would be appreciated.

I will explain something first: the navigation object gets passed to the prop object when this component rendered as a screen for any navigator and not next to it
so you can do a couple of things now
1- const Layout = (props) and you can access the navigation like how you access the children like this props.navigation.navigate('Index')
2- or you can simply destructure anything you want from the props object like this: const Layout = ({children, navigation}) => { this way you can type less as you don't have to reference the props object anymore and it will be navigation.navigate('Index')
3- if your component does not rendered by a navigator, then the navigation object will not be passed to the prop object and it will be undefined -- in this case there is a simple solution you can do and it's not the only solution:
I see that you already importing useNavigation and useNavigation exposes the navigation object out of the box for you, so what you can do inside your component is: const navigation = useNavigation() then you can use navigation as always, but don't forget to remove the navigation from the component parameters
here you can find more details about the useNavigation hook useNavigation
4- here I will add the code for the layout component which I believe it should work
import { Container, Header, Content, Left, Right } from 'native-base';
import React from 'react';
import { View } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import { useNavigation } from '#react-navigation/native';
const Layout = (props) => {
const navigation = useNavigation();
return (
<Container >
<Header style={{ alignItems: 'center', backgroundColor: '#3a455c', height: 50 }}>
<Left style={{ flexDirection: 'row' }}>
<MaterialIcon style={{ color: 'white', marginRight: 10 }} onPress={ () => console.log(navigation) } size={30} color="black" name= 'menu' />
</Left>
<Icon onPress={ () => navigation.navigate('Index') } size={20} style={{marginTop: 4, marginLeft: 130}} color="white" name= 'home' />
<Right style={{ flexDirection: 'row' }}>
<Icon onPress={ () => navigation.navigate('Index') } size={20} style={{marginTop: 4}} color="green" name= 'user' />
</Right>
</Header>
<Content >
<View>
{ props.children }
</View>
</Content>
</Container>
);
}
export default Layout;
there other options like passing a handler to onPress and executing this function from the component where has access to the navigation object
I hope this is useful to you. let me know if it works

Related

SplashScreen and Navigation for react-native for both iOS and Android (3 screens)

I am currently working on a project where I need to include SplashScreen to the main page and transitioning to navigate to another screen by using TouchableOpacity texts.
Problem is when I started compiling the code, the error message showed up saying "Error: App(...) : Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null."
I have basically installed all the pod installs and other necessary tools (such as react-navigation) that are required to create the navigation function to work.
And no, I have not initiated the project with expo.
I have just started stepping on the path of coding and this is my novice code.
But if there's any help I would be very thankful.
import React, {Fragment, useEffect, useState} from 'react';
import type {Node} from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
TouchableOpacity,
Alert,
View,
Button
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
import SplashScreen from 'react-native-splash-screen';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
const App: () => Node = () => {
const isDarkMode = useColorScheme() === 'dark';
useEffect(() => {
setTimeout(() => {
SplashScreen.hide();
},1000);
}, [])
const mainpage = ({navigation}) => {
return (
<View style={{ flex:1, backgroundColor: 'black', justifyContent: 'center', padding: 40, }}>
<TouchableOpacity onPress={() => navigation.navigate('ITALY')}>
<Text style={{ color: 'white', fontSize: 20, textAlign: 'justify', padding: 40,}}>ITALY</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate('SPAIN')}>
<Text style={{ color: 'white', fontSize: 20, textAlign: 'justify', padding: 40,}}>SPAIN</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate('FRANCE')}>
<Text style={{ color: 'white', fontSize:20, textAlign: 'justify', padding: 40,}}>FRANCE</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate('SWEDEN')}>
<Text style={{ color: 'white', fontSize: 20, textAlign: 'justify', padding: 40,}}>SWEDEN</Text>
</TouchableOpacity>
</View>
)
};
const ITALY = ({navigation}) => {
return (
<View style={{backgroundColor: 'black'}}>
<Text style={{justifyContent: 'center', color: 'white',}}>ITALY</Text>
</View>
)
}
const SPAIN = ({navigation}) => {
return (
<View style={{backgroundColor: 'black'}}>
<Text style={{justifyContent: 'center', color: 'white'}}>SPAIN</Text>
</View>
)
}
const FRANCE = ({navigation}) => {
return (
<View style={{backgroundColor: 'black'}}>
<Text style={{justifyContent: 'center', color: 'white'}}>FRANCE</Text>
</View>
)
}
const SWEDEN = ({navigation}) => {
return (
<View style={{backgroundColor: 'black'}}>
<Text style={{justifyContent: 'center', color: 'white'}}>SWEDEN</Text>
</View>
)
}
const Stack = createNativeStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name ="ITALY" component={ITALY} />
<Stack.Screen name ="SPAIN" component={SPAIN} />
<Stack.Screen name="FRANCE" component={FRANCE} />
<Stack.Screen name="SWEDEN" component={SWEDEN} />
</Stack.Navigator>
</NavigationContainer>
);
};
};
export default App;
As you can see there are two const App variable from SplashScreen timing and to the navigation container.
I think this is where the problem started. Having to do with two same name (which is App) as consts
Also, I have recklessly added big loads of imports in case of additional error I could face; but I think it would be the safest way not to touch in those areas; And yes, there are lots of const variables which was the only way I could barely manage.
The vision for this code to work in a way is to
First: SplashScreen (first screen) appears when clicking on an app
Second: After SplashScreen is loaded, open the main page (second screen) where 4 countries (ITALY, SPAIN, FRANCE, SWEDEN) of strings (or texts with TouchableOpacity) are aligned as a list of item
from left hand side of the main page
Third: When clicking on each countries (texts or strings), it navigates to the (third screen)
where default name of the country is justified in the center as a text. Also not to forget the function that goes back to the main page.
Many thanks in advance.

How to fix Require cycle warning In React Expo

I'm getting this error.[ErrorImg][1]
This is Require cycles error but I can't find what is the reason.
I want to use ListItem component to View.js through component.js and I already use that like this import {ListItem } from '../components/';.
The result is good but there is this warning and I want to fix. Please help me.
Require cycle: Components.js->
import Button from './Button';
import Select from './Select';
import Icon from './Icon';
import Tabs from './Tabs';
import Product from './Product';
import Drawer from './Drawer';
import Header from './Header';
import Switch from './Switch';
import ListItem from './ListItem';
import HorizontalListItem from './HorizontalListItem';
export {
Button,
Select,
Icon,
Tabs,
Product,
Drawer,
Header,
Switch,
ListItem,
HorizontalListItem,
};```
View.js->
```const renderPatientsList = () => {
return(
<Block style={{ paddingHorizontal: theme.SIZES.BASE }}>
<ScrollView vertical={true} showsVerticalScrollIndicator={false} style={{marginBottom: theme.SIZES.BASE * 3}}>
<ListItem product={products[0]} horizontal />
<ListItem product={products[1]} horizontal />
<ListItem product={products[2]} horizontal />
</ScrollView>
</Block>
)
}```
ListItem.js -->
```import React from 'react';
import { withNavigation } from '#react-navigation/compat';
import { StyleSheet, Dimensions, Image, TouchableWithoutFeedback } from 'react-native';
import { Block, Text, theme } from 'galio-framework';
import { Icon } from '../components/';
import { LinearGradient } from 'expo-linear-gradient';
const { width } = Dimensions.get('screen');
const ListItem = props => {
const { navigation, product, horizontal, full, style, priceColor, imageStyle, time, unReaden, weekday } = props;
const imageStyles = [styles.image, full ? styles.fullImage : styles.horizontalImage, imageStyle];
return (
<Block row={horizontal} card flex style={[styles.product, styles.shadow, style]}>
<TouchableWithoutFeedback onPress={() => navigation.navigate('Product', { product: product })}>
<Block style={[styles.imageContainer, styles.shadow]}>
<Image source={{ uri: product.image }} style={imageStyles}/>
</Block>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={() => navigation.navigate('Product', { product: product })}>
<Block flex={3}>
<Text size={16} style={styles.userName}>{product.title}</Text>
<Block flexDirection={'row'}>
<Icon name="photo" family="font-awesome" color={theme.COLORS.MUTED} size={theme.SIZES.BASE} style={styles.icons}> </Icon>
<Icon name="check" family="font-awesome" color={theme.COLORS.MUTED} size={theme.SIZES.BASE} style={styles.icons}> </Icon>
<Text size={16} muted={!priceColor} color={priceColor}>${product.price}</Text>
</Block>
</Block>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={() => navigation.navigate('Product', { product: product })}>
<Block flex={1}>
<>
{(product.time) ? (
<Text size={12} style={styles.times} color={"#06D81E"}>{product.time}</Text>
) : (
<Text size={12} style={styles.times} color={"#000"}>{product.weekday}</Text>
)}
</>
<Block style={{borderRadius: 100, backgroundColor: "#06D81E", width: theme.SIZES.BASE * 1.2, height: theme.SIZES.BASE * 1.2, position: "absolute", right: theme.SIZES.BASE, bottom: theme.SIZES.BASE}}>
<Text size={12} center style={{justifyContent: 'center', alignItems: 'center'}} color={"#FFF"} fontWeight={"semiBold"}>{product.unReaden}</Text>
</Block>
</Block>
</TouchableWithoutFeedback>
</Block>
);
}```
[1]: https://i.stack.imgur.com/rDB6i.png
You are getting require cycle warning because you end up creating a loop ( require from Component.js in ListItem.js and require from ListItem.js in Component.js)
In ListItem.js,
import {Icon} from Icon.js
The general idea is to write the module in another file and import that module from there.
see this for a detailed explanation:
Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle

React Native multiple use of component defined in a parent class

Below is the code for a Header which I've defined in a common.js file:
class HeaderStyle extends Component {
render() {
return (
<Header style={{ elevation: 5 }}>
<Left style={{ flex: 1 }}>
<Icon name="md-menu" size={30} onPress={() => this.props.navigation.openDrawer()} />
</Left>
<Body style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Image style={{ width: 80, height: 45, }} source={require('./resources/hr-pro-logo.png')} />
</Body>
<Right style={{ flex: 1 }}>
<Icon name="md-search" size={30} onPress={() => alert('hi there')} />
</Right>
</Header>
);
}
}
And here is the code for my DashboardScreen.js:
export default class DashboardScreen extends Component {
render() {
return (
<View style={{ flex: 1 }}>
<HeaderStyle /> // This is where I'm using the Header imported from common.js
<View>
<FlatList
// Code to populate my list
/>
</View>
</View>
);
}
I've been able to import the Header in my Dashboard, but when I click on the menu icon onPress={() => this.props.navigation.openDrawer()} it throws the error undefined is not an object (evaluating '_this.props.navigation.openDrawer')
Appreciate it if anyone can assist me with this issue, I'd like to be able to open the drawer on clicking the menu icon.
FYI - When I used the Header code directly in my Dashboard, the app ran smoothly with no errors. But since there are multiple screens where I want to use the Header, I need to keep it in a common place to avoid repetition of coding.
You must pass the navigation prop to your component:
<HeaderStyle navgation={this.props.navigation} />
Or, you could use the withNavigation function from react-navigation:
import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';
class MyBackButton extends React.Component {
render() {
return (
//you Header render
);
}
}
// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);
The documentation is here

React Native + Redux - onPress dispatch an action and another function

I have a list of team members that onPress will navigate to the next screen using react-navigation/StackNavigator. Depending on which team member is clicked, the next screen will have dynamically loaded the relevant team member information.
I have been able to achieve this by using Redux to change the state of who has been selected. However at the moment I am unable to figure how I can dispatch the action and trigger the function to navigate to the next screen.
Below is my component that holds the events in which I want to dispatch the action and navigation function. You can see the first team member 'Tim' has the navigation function. All other team members will dispatch their individual action types.
I'm fairly new to Redux I would appreciate if anyone could point me in the right direction of how to add these together.
The Repository can be found here - Github
import React, {Component} from 'react';
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native';
const styles = StyleSheet.create({
pane: {
width: 300,
height: 50,
padding: 10,
backgroundColor: 'lightgray',
alignItems: 'center',
justifyContent: 'center',
margin: 3
}
});
function navTO(passBack, location) {
const { navigate } = passBack.props.navigation;
navigate(location)
}
export default class TeamList extends Component {
constructor(props) {
super(props);
}
render() {
const { passBack, location, whoSelected, Tim, Kate, Ian, Smita, Lee, Ben } = this.props;
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{whoSelected}</Text>
<TouchableOpacity onPress={() => navTO(passBack, location)} style={styles.pane}>
<Text>Tim</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Kate} style={styles.pane}>
<Text>Kate</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Ian} style={styles.pane}>
<Text>Ian</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Smita} style={styles.pane}>
<Text>Smita</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Lee} style={styles.pane}>
<Text>Lee</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Ben} style={styles.pane}>
<Text>Ben</Text>
</TouchableOpacity>
</View>
);
}
}
How about something like:
export default class TeamList extends Component {
constructor(props) {
super(props);
this.showTeamMember = this.showTeamMember.bind(this)
}
showTeamMember(teamMemberCallback) {
const { passBack, location } = this.props;
return () => {
teamMemberCallback();
navTO(passBack, location);
}
}
render() {
const { Tim, Kate, Ian, Smita, Lee, Ben } = this.props;
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{whoSelected}</Text>
<TouchableOpacity onPress={this.showTeamMember(Tim)} style={styles.pane}>
<Text>Tim</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Kate)} style={styles.pane}>
<Text>Kate</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Ian)} style={styles.pane}>
<Text>Ian</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Smita)} style={styles.pane}>
<Text>Smita</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Lee)} style={styles.pane}>
<Text>Lee</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Ben)} style={styles.pane}>
<Text>Ben</Text>
</TouchableOpacity>
</View>
);
}
}
showTeamMember creates the handler function for onPress that call both the teamMemberCallback and the navTo function.

Missing styles for item in React Native Side Menu (should indicate active route in React Native Navigator)

I'm working on an app with a Side Menu and a Navigator. In the side menu there are menu-items which let's you navigate (using Navigator), and the menu-items also get styled to indicate which one is active.
When first going to a new route with navigator.push() and then going back with navigator.pop(), the menu-item corresponding to the previously active route (which is now inactive) does not get either of the styles to show that it is either inactive or active. The style of the menu item (RouteMenuItem in the full example below) is as follows
style={[
{ padding: 15, borderColor: 'firebrick', borderWidth: 1 },
(isActive
? {backgroundColor: 'green'}
: {opacity: 0.5}
)
]}
How is it possible that neither {backgroundColor: 'green'} nor {opacity: 0.5} gets applied?
The image below shows how the bug looks on Android: "Route ONE" is selected, however, the menu item for "Route TWO" does not have opacity: 0.5 as it should (it should be half transparent like the 3rd menu item).
Below is the full code for a minimal example to reproduce the bug. The component hierarchy is like this:
<Navigator renderScene={() => <SideMenu><View /></SideMenu>} />
PS: We use StyleSheet.create() in our code, but in the example below I've just defined the styles inline. It does not seem to make any difference.
import React from 'react';
import {View, Text, TouchableHighlight, Navigator, Dimensions} from 'react-native';
import SideMenu from 'react-native-side-menu';
/***************
/** Routes **/
/****************/
const ROUTES = {
ONE: () => ({ title: 'Route ONE' }),
TWO: () => ({ title: 'Route TWO' }),
THREE: () => ({ title: 'Route THREE' }),
};
/**************/
/** Main **/
/**************/
export default class Main extends React.Component {
render() {
return (
<Navigator
style={{flex: 1}}
initialRouteStack={[
ROUTES.ONE(),
]}
renderScene={(route, navigator) =>
<Scene route={route} navigator={navigator} />
}
/>
);
}
}
/***************/
/** Scene **/
/***************/
class Scene extends React.Component {
state = {
menuIsOpen: false,
}
openMenu = () => {
this.setState({ menuIsOpen: true });
}
onMenuOpenChanged = (menuIsOpen) => {
if (this.state.menuIsOpen !== menuIsOpen) {
this.setState({ menuIsOpen });
}
}
render() {
const {route, navigator} = this.props;
return (
<View style={{flex: 1}}>
<SideMenu
menu={
<Menu
route={route}
navigator={navigator}
/>
}
menuPosition="right"
bounceBackOnOverdraw={false}
onChange={this.onMenuOpenChanged}
isOpen={this.state.menuIsOpen}
openMenuOffset={Dimensions.get('window').width * 0.75}
disableGestures={false}
>
<Screen route={route} navigator={navigator} openMenu={this.openMenu} menuIsOpen={this.state.menuIsOpen} />
</SideMenu>
</View>
);
}
}
/**************/
/** Menu **/
/**************/
class Menu extends React.Component {
render() {
const {route, navigator} = this.props;
return (
<View style={{ flex: 1, backgroundColor: 'coral', paddingTop: 25 }}>
<Text>Currently at {route.title}</Text>
<RouteMenuItem forRoute={ROUTES.ONE()} route={route} navigator={navigator} />
<RouteMenuItem forRoute={ROUTES.TWO()} route={route} navigator={navigator} />
<RouteMenuItem forRoute={ROUTES.THREE()} route={route} navigator={navigator} />
</View>
);
}
}
const RouteMenuItem = ({forRoute, route, navigator}) => (
<TouchableHighlight onPress={() => navigator.push(forRoute)}>
<Text style={[ { padding: 15, borderColor: 'firebrick', borderWidth: 1 }, (forRoute.title === route.title ? {backgroundColor: 'green'} : {opacity: 0.5}) ]}>
Go to {forRoute.title} ({(forRoute.title === route.title ? 'current route' : 'NOT CURRENT ROUTE')})
</Text>
</TouchableHighlight>
);
/*****************************/
/** Screen, inside Menu **/
/*****************************/
class Screen extends React.Component {
render() {
const {route, navigator, openMenu, menuIsOpen} = this.props;
return (
<View style={{ flex: 1 }}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', backgroundColor: 'peachpuff', paddingTop: 25 }}>
<HeaderButton onPress={navigator.pop}>Back</HeaderButton>
<HeaderButton onPress={openMenu}>Menu</HeaderButton>
</View>
<View style={{ flex: 1, backgroundColor: 'white' }}>
<Text style={{ margin: 50, fontSize: 50 }}>
{route.title}
</Text>
</View>
</View>
);
}
}
const HeaderButton = ({onPress, children}) => (
<TouchableHighlight underlayColor="green" onPress={onPress}>
<Text style={{ padding: 10, borderColor: 'firebrick', borderWidth: 1 }}>
{children}
</Text>
</TouchableHighlight>
);
The problem occurs becauase children of TouchableHighlight gets default opacity (1) after the TouchableHighlight is pressed. As that is a more specific problem I've asked a new question here.
In this case the bug appears when going back to a previous route, because the menu item in that instance of the Menu was pressed (moving us to a new route before we could spot the bug).