React Native multiple use of component defined in a parent class - react-native

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

Related

React Navigation6 Header issue

i'm building application with ReactNative and Expo, i building component called Header showing the title of each screen,
so i send props of name on each time render the component
<Header title={"Home Screen"}/>
Header.js comoment
import React,{ useState } from 'react';
import { Text, View, Image,StyleSheet} from 'react-native';
import Icon from 'react-native-vector-icons/Feather';
import { useNavigation } from "#react-navigation/native";
const Header=( navigation, title) =>{
const { navigate } = useNavigation();
return(
<View style={styles.container}>
<Icon name="user" size={28}/>
<Text style={{fontWeight:'bold'}} >{title}</Text>
<Icon name="bell" size={28} />
</View>
)
}
export default Header;
const styles = StyleSheet.create({
container:{
position:"absolute",
display:'flex',
justifyContent: 'space-between',
flexDirection:'row',
height: 60,
top:10 ,
left:6,
right:6,
elevation:0,
backgroundColor: '#fff',
alignItems: 'center',
}
})
this error showing:
Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
You just missed to de-structure your props in Header. Simply change your Header component to have
const Header=({ title }) =>{ // added {} to de-structure your props
const { navigate } = useNavigation();
return(
<View style={styles.container}>
<Icon name="user" size={28}/>
<Text style={{fontWeight:'bold'}} >{title}</Text>
<Icon name="bell" size={28} />
</View>
)
}

Passing props and navigation in react navigation

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

React native layout misbehaving

I am trying to learn React native with Ignite. Been fighting with the layout.
Here is my main container render function:
render () {
return (
<View style={styles.mainContainer}>
<Image source={Images.background} style={styles.backgroundImage} resizeMode='stretch' />
<View style={[styles.container]}>
<View style={styles.section} >
{/* <Image source={Images.ready} />*/}
<Text style={styles.sectionText}>
Tap to randomly choose your training task. Slack off for 5
</Text>
</View>
<View style={styles.centered}>
<TouchableOpacity onPress={this._onPressButton}>
<Image source={Images.launch} style={styles.logo} />
</TouchableOpacity>
</View>
</View>
<View style={[styles.bottom]}>
<View >
<BottomBar />
</View>
</View>
</View>
)
}
In particular, the last sibling of the container has a view with a BottomBar component.The bottom style does this:
bottom: {
justifyContent: 'flex-end',
marginBottom: Metrics.baseMargin
}
the BottomBar component:
import React, { Component } from 'react'
// import PropTypes from 'prop-types';
import { View, Text, TouchableOpacity } from 'react-native'
import styles from './Styles/BottomBarStyle'
import Icon from 'react-native-vector-icons/FontAwesome'
export default class BottomBar extends Component {
// // Prop type warnings
// static propTypes = {
// someProperty: PropTypes.object,
// someSetting: PropTypes.bool.isRequired,
// }
//
// // Defaults for props
// static defaultProps = {
// someSetting: false
// }
render () {
console.tron.log('rendering my component')
console.tron.log('Bottom bar styles: \n',styles)
return (
<View style={[styles.iconsContainer, styles.debugGreen]}>
<TouchableOpacity style={[styles.icons,styles.debugYellow]} onPress={()=>{console.tron.log('rocket')}} >
<Icon style={styles.icons} name='rocket' size={40} color='white' />
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={ ()=>{console.tron.log('send')} }>
<Icon style={styles.icons} name='send' size={40} color='white' />
</TouchableOpacity>
</View>
)
}
}
the styles associated with it:
import { StyleSheet } from 'react-native'
import DebugStyles from '../../Themes/DebugStyles'
import { Metrics } from '../../Themes/'
export default StyleSheet.create({
...DebugStyles,
iconsContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
height: 45,
borderRadius: 5,
marginHorizontal: Metrics.section,
marginVertical: Metrics.baseMargin
},
icons:{
height: 45
}
})
The issue I have, is that if I saw that bottomBar component for a Rounded button as such:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { TouchableOpacity, Text } from 'react-native'
import styles from './Styles/RoundedButtonStyles'
import ExamplesRegistry from '../Services/ExamplesRegistry'
// Note that this file (App/Components/RoundedButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.
// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample('Rounded Button', () =>
<RoundedButton
text='real buttons have curves'
onPress={() => window.alert('Rounded Button Pressed!')}
/>
)
console.tron.log('Rounded button style: ',styles)
export default class RoundedButton extends Component {
static propTypes = {
onPress: PropTypes.func,
text: PropTypes.string,
children: PropTypes.string,
navigator: PropTypes.object
}
getText () {
const buttonText = this.props.text || this.props.children || ''
return buttonText.toUpperCase()
}
render () {
console.tron.log('roundedButton styles:', styles)
return (
<TouchableOpacity style={styles.button} onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.getText()}</Text>
</TouchableOpacity>
)
}
}
with its styles:
import { StyleSheet } from 'react-native'
import { Fonts, Colors, Metrics } from '../../Themes/'
export default StyleSheet.create({
button: {
height: 45,
borderRadius: 5,
marginHorizontal: Metrics.section,
marginVertical: Metrics.baseMargin,
backgroundColor: Colors.fire,
justifyContent: 'center'
},
buttonText: {
color: Colors.snow,
textAlign: 'center',
fontWeight: 'bold',
fontSize: Fonts.size.medium,
marginVertical: Metrics.baseMargin
}
})
I get the expected view :
However, with my BottomBar component I get:
One thing to notice is that the debugGreen style is just a border that should wrap around my BottomBar component and it is shown flat, but the icons within it render lower, and the debugYellow styled box around the icon is shown around the icon as expected, just shifted a whole way down.
If your mainContainer's view is flex : 1 or height : 100%, you should divide the child's height by 8:2 or the flex by 8:2.
Example
<View style={styles.mainContainer}> // flex: 1
<View style={styles.container}> // flex : 0.8
...
</View>
<View style={styles.bottom}> // flex : 0.2
<BottomBar />
</View>
</View>

No <Image> component and I get the <Image> component cannot contain children

This question has been asked but this scenario is different.
The entire error is: Error: The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning. Which is very self explanatory. However it doesn't help me because I don't have an Image component in my code, I've check and nowhere is there an Image component being called.
The error being displayed is saying it's here:
This error is located at:
in NavigationContainer (at App.js:25)
in App (at withExpoRoot.js:22)
Here is my code for App.js
import React from 'react';
import Expo from "expo";
import { AppLoading } from "expo";
import HomeScreen from "./src/HomeScreen/index.js";
export default class App extends React.Component {
constructor() {
super();
this.state = {
isReady: false
};
}
async componentWillMount() {
// await Expo.Font.loadAsync({
// Roboto: require("native-base/Fonts/Roboto.ttf"),
// Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"),
// Ionicons: require("native-base/Fonts/Ionicons.ttf")
// });
this.setState({ isReady: true });
}
render() {
if (!this.state.isReady) {
return <AppLoading />;
}
return <HomeScreen />;
}
}
HomeScreen.js
import React from "react";
import { StatusBar } from "react-native";
import { Container, Header, Title, Left, Icon, Right, Button, Body, Content, Text, Card, CardItem } from "native-base";
export default class HomeScreen extends React.Component {
render() {
return (
<Container>
<Header>
<Left>
<Button
transparent
onPress={() => this.props.navigation.navigate("DrawerOpen")}>
<Icon name="menu" />
</Button>
</Left>
<Body>
<Title>HomeScreen</Title>
</Body>
<Right />
</Header>
<Content padder>
<Card>
<CardItem>
<Body>
<Text>Chat App to talk someawesome people</Text>
</Body>
</CardItem>
</Card>
<Button full rounded dark
style={{ marginTop: 10 }}
onPress={() => this.props.navigation.navigate("Chat")}>
<Text>Chat With People</Text>
</Button>
<Button full rounded primary
style={{ marginTop: 10 }}
onPress={() => this.props.navigation.navigate("Profile")}>
<Text>Goto Profiles</Text>
</Button>
</Content>
</Container>
);
}
}
As you can see nowhere here are we calling an Component which is the part I can't figure out. Any ideas?
Ok I'm answering my own question for someone else who might encounter.
React Native gives the error that
This error is located at:
in NavigationContainer (at App.js:25)
in App (at withExpoRoot.js:22)
However this error is located in a file that is in a subcomponent in my case:
Sidebar.js
return (
<Container>
<Content>
<Image
source={{
uri: "https://github.com/GeekyAnts/NativeBase-KitchenSink/raw/react-navigation/img/drawer-cover.png"
}}
style={{
height: 120,
alignSelf: "stretch",
justifyContent: "center",
alignItems: "center"
}}>
<Image
square
style={{ height: 80, width: 70 }}
source={{
uri: "https://github.com/GeekyAnts/NativeBase-KitchenSink/raw/react-navigation/img/drawer-cover.png"
}}
/>
</Image>
<List
dataArray={routes}
renderRow={data => {
return (
<ListItem
button
onPress={() => this.props.navigation.navigate(data)}>
<Text>{data}</Text>
</ListItem>
);
}}
/>
</Content>
</Container>
);
I then changed <Image> to <ImageBackground> and it works.

Is it possible to add icons to Native Base tabs?

Native Base docs only shows how to change background color, text color and font size. But it seems not possible to add icons to tabs.
Is it possible or I will need to fork and implement myself?
Thank you.
With NativeBase 2.0 and above you can add icons to Tabs using TabHeading tags inside heading property.
<Content>
<Tabs>
<Tab heading={<TabHeading><Icon name='settings'/></TabHeading>}>
<Tab heading={<TabHeading><Icon name='home'/></TabHeading>}>
</Tabs>
</Content>
You need to implement yourself. I have implemented this functionality. Please have a look if that would be help you.
Create tabs.js
import React from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,RefreshControl
} from 'react-native';
import IconTabs from 'react-native-vector-icons/Ionicons';
import NavigationBar from 'react-native-navbar';
import { Container, Header, Title, Button,Icon } from 'native-base';
const Tabs = React.createClass({
tabIcons: [],
propTypes: {
goToPage: React.PropTypes.func,
activeTab: React.PropTypes.number,
tabs: React.PropTypes.array,
},
componentDidMount() {
this._listener = this.props.scrollValue.addListener(this.setAnimationValue);
},
setAnimationValue({ value, }) {
this.tabIcons.forEach((icon, i) => {
const progress = (value - i >= 0 && value - i <= 1) ? value - i : 1;
});
},
render() {
return (
<View>
<View style={[styles.tabs, this.props.style, ]}>
{this.props.tabs.map((tab, i,) => {
return <TouchableOpacity key={tab} onPress={() => this.props.goToPage(i)} style={styles.tab}>
<IconTabs
name={tab}
size={20}
color={this.props.activeTab === i ? 'rgb(255,255,255)' : 'rgb(189, 224, 250)'}
ref={(icon) => { this.tabIcons[i] = icon; }}
/>
<Text style={{fontWeight:'bold', fontSize:10, color:this.props.activeTab === i ? 'rgb(255,255,255)' : 'rgb(189, 224, 250)'}}>{`${this.props.name[i]}`}</Text>
</TouchableOpacity>;
})}
</View>
</View>
);
},
});
const styles = StyleSheet.create({
tab: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingBottom: 10,
},
tabs: {
height: 50,
flexDirection: 'row',
paddingTop: 5,
borderWidth: 0,
borderTopWidth: 0,
borderLeftWidth: 0,
borderRightWidth: 0,
backgroundColor: '#2196F3',
},
});
export default Tabs;
And use this component in your view like following.
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
ScrollView,Navigator
} from 'react-native';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import Tabs from './tabs';
export default class LeavesTab extends Component{
constructor(props) {
super(props);
}
_navigate(name) {
this.props.navigator.push({
name: name,
passProps: {
name: name
}
})
}
render() {
let Tabname = ["Tab1","Tab2","Tab3","Tab4"];
return (
<ScrollableTabView
initialPage={this.props.index}
renderTabBar={() => <Tabs name={Tabname} navigator={this.props.navigator} showHeader={true} />}
>
<ScrollView tabLabel="md-calendar">
<Requests tabLabel='Requests' navigator={this.props.navigator} />
</ScrollView>
<ScrollView tabLabel="md-checkbox">
<LeaveList tabLabel='Approved' navigator={this.props.navigator} />
</ScrollView>
<ScrollView tabLabel="md-time">
<LeaveList tabLabel='Pending' navigator={this.props.navigator} />
</ScrollView>
<ScrollView tabLabel="md-close-circle">
<LeaveList tabLabel='Rejected' navigator={this.props.navigator} />
</ScrollView>
</ScrollableTabView>
);
}
}
const styles = StyleSheet.create({
});
Santosh's answer is right, but I found a cleaner way to implement this based on Native Base tabs.
A rendering tab component is necessary, like in Santosh's example.
But in the component, instead of using the ScrollableTabView, I can use React Native's Tabs component. An example:
export default class App extends Component {
render() {
return (
<Container>
<Header>
<Title>Header</Title>
</Header>
<Content>
<Tabs renderTabBar={() => <TabBar />}>
<One tabLabel="video-camera" />
<Two tabLabel="users" />
</Tabs>
</Content>
<Footer>
<FooterTab>
<Button transparent>
<Icon name="ios-call" />
</Button>
</FooterTab>
</Footer>
</Container>
);
}
}
EDIT
#KumarSanketSahu said that version 2.0 is comming with the ability of changing icons in the tabs. My answer above is for version 0.5.x.