Change active state and flex transition - reactjs

I have a react component class like below
<View style={{flex: 1}}>
<TouchableOpacity style={styles.FreeCoffee}/>
<TouchableOpacity style={styles.Help}/>
</View>
both touchableopacity components have the value of flex 2 so they equally divided in window. When one of the touchableopacity pressed, I want to make a transition between flex to 2 into 4 so that one box can grow with animation, also mark it as a "active" or "selected" one. I have searched this many many times but since I am a beginner in ReactNative, I couldn't find any proper way to that.
Is this possible or achiveable ?
//Edit Full Code
import React from 'react'
import {ScrollView, Text, View, TouchableOpacity, Button} from 'react-native'
import styles from '../Styles/Containers/HomePageStyle'
export default class HomePage extends React.Component {
constructor(props){
super(props);
/*this.state = {
active : { flex : 8 }
}*/
}
render() {
return (
<View style={styles.mainContainer}>
<View style={{flex: 1}}>
<TouchableOpacity style={styles.FreeCoffee}/>
<TouchableOpacity style={styles.Help}/>
</View>
</View>
)
}
componentWillMount(){
}
animateThis(e) {
}
}

You can use LayoutAnimation to do this. Define state that toggles the styles that are applied to your render and use onPress in the TouchableOpacity to define your function that Calls the LayoutAnimation and setState. Something like the following:
import React from 'react';
import { LayoutAnimation, ScrollView, StyleSheet, Text, View, TouchableOpacity, Button } from 'react-native';
// import styles from '../Styles/Containers/HomePageStyle'
const styles = StyleSheet.create({
mainContainer: {
flexGrow: 1,
},
FreeCoffee: {
backgroundColor: 'brown',
flex: 2,
},
Help: {
backgroundColor: 'blue',
flex: 2,
},
active: {
flex: 4,
borderWidth: 1,
borderColor: 'yellow',
},
});
export default class HomeContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
active: 'neither',
};
this.setActive = this.setActive.bind(this);
}
setActive(active) {
// tells layout animation how to handle next onLayout change...
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
// set active in state so it triggers render() to re-render, so LayoutAnimation can do its thing
this.setState({
active,
});
}
render() {
return (
<View style={styles.mainContainer}>
<View style={{ flex: 1, backgroundColor: 'pink' }}>
<TouchableOpacity
style={[
styles.FreeCoffee,
(this.state.active === 'FreeCoffee') && styles.active]}
onPress={() => {
this.setActive('FreeCoffee');
}}
/>
<TouchableOpacity
style={[
styles.Help,
(this.state.active === 'Help') && styles.active]}
onPress={() => {
this.setActive('Help');
}}
/>
</View>
</View>
);
}
}

Related

React Navigation - pop() go's back to root rather than previous page

I am trying to add a back buttons into a bespoke sidemenu component.
Here is my navigation.js setup:
import { createAppContainer, createStackNavigator, createDrawerNavigator } from 'react-navigation';
import App from '../components/App';
import HomeView from '../components/HomeView';
import CheckIn from '../components/CheckIn';
import LoginView from '../components/LoginView';
import LrmDocs from '../components/LrmDocs';
import Rota from '../components/Rota';
import SideMenu from '../components/util/SideMenu';
const InitStack = createStackNavigator({
AppContainer: App,
LoginViewContainer:LoginView,
});
const RootStack = createDrawerNavigator({
Auth:InitStack,
HomeViewContainer: HomeView,
RotaContainer: Rota,
CheckInContainer:CheckIn,
LrmDocsContainer: LrmDocs,
},
{
drawerPosition: 'right',
contentComponent: SideMenu,
cardStyle: { backgroundColor: '#FFFFFF'},
drawerLockMode: 'locked-closed'
}
);
let Navigation = createAppContainer(RootStack);
export default Navigation;
I have the following setup in my bespoke sidemenu -
mport React, {Component} from 'react';
import CopyrightSpiel from './CopyrightSpiel';
import {ScrollView, Text, View, StyleSheet, Image, Button, TouchableOpacity} from 'react-native';
import { withNavigation } from 'react-navigation';
import { connect } from "react-redux";
import { authLogout, clearUser } from "../../store/actions/index";
class SideMenu extends Component {
constructor(props) {
super(props);
this.state = { loggedIn:false};
}
logOutHandler = () => {
this.props.onTryLogout();
this.props.clearUser();
this.props.navigation.navigate('AppContainer');
};
render() {
const isLoggedIn = () => {
if(this.state.loggedIn){
return true;
}
else {return false; }
};
let cp = this.props.activeItemKey;
let getCurrentCoordinates = (pg) => {
if(cp === pg){
return true;
}
};
return (
<View style={styles.container}>
<ScrollView>
<View style={styles.header}>
<View style={styles.closeBtnWrap}>
<TouchableOpacity
onPress={() => this.props.navigation.toggleDrawer() }
>
<Image
style={styles.closeBtnImg}
source={require('../../images/icons/ico-close.png')}
/>
</TouchableOpacity>
</View>
<View style={styles.logoBlock}>
<Image style={styles.homeBlockImg} source={require('../../images/loginLogo.png')} />
</View>
</View>
<View style={styles.navSection}>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('HomeViewContainer')}
>
<View style={[styles.navSectionStyle, getCurrentCoordinates('HomeViewContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle}>
HOME
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('RotaContainer')}
>
<View style={[styles.navSectionStyle, getCurrentCoordinates('RotaContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle} >
MY ROTA
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('LrmDocsContainer')}
>
<View style={[styles.navSectionStyle, getCurrentCoordinates('LrmDocsContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle} >
LRM DOCS
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={this.logOutHandler}>
<View style={styles.navSectionStyle}>
<Text style={styles.navItemStyle} >
SIGN OUT
</Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.navSection}>
<Text style={styles.navSectionTitle}>Current Shift Options:</Text>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('CheckInContainer')}
>
<View style={[styles.navSectionStyle, isLoggedIn() && styles.checkedInHide, getCurrentCoordinates('CheckInContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle} >
CHECK IN
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('CheckInContainer')}
>
<View style={[styles.navSectionStyle, !(isLoggedIn()) && styles.checkedOutHide, getCurrentCoordinates('CheckInContainer') && styles.navItemSel]}>
<Text style={styles.navItemStyle} >
CHECK OUT
</Text>
</View>
</TouchableOpacity>
</View>
</ScrollView>
<View style={styles.footerContainer}>
<CopyrightSpiel color="LightGrey"/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
header:{
flexDirection:'row',
justifyContent:'center',
},
closeBtnWrap:{
position:'absolute',
left:15,
top:0,
opacity:0.8,
},
closeBtnImg:{
width:22,
height:22
},
container:{
backgroundColor:'#1C2344',
alignSelf:'stretch',
flex:1,
paddingTop:50,
borderLeftColor:'#F7931E',
borderLeftWidth:1
},
logoBlock:{
alignSelf:'center',
},
homeBlockImg:{
marginTop:10,
width:60,
height:105,
},
navSectionStyle:{
alignItems:'stretch',
textAlign: 'center',
backgroundColor:'rgba(255,255,255,0.1)',
paddingTop:8,
paddingBottom:8,
marginTop:15,
},
navItemSel:{
backgroundColor:'rgba(255,255,255,0.9)',
},
navItemSelText:{
color:'#1C2344',
},
navItemStyle:{
color:'#F7931E',
fontSize:24,
alignSelf:'center'
},
navSection:{
marginTop:30
},
navSectionTitle:{
color:'rgba(255,255,255,0.5)',
marginLeft:15,
},
footerContainer:{
paddingBottom:15,
},
checkedInHide:{
display:'none',
},
checkedOutHide:{
display:'none',
},
});
const mapDispatchToProps = dispatch => {
return {
onTryLogout: () => dispatch(authLogout()),
clearUser: () => dispatch(clearUser())
};
};
export default connect(null, mapDispatchToProps)(withNavigation(SideMenu));
which works fine -
I then have the following in my sub view header:
import React from 'react';
import {View, Image, Text, StyleSheet, TouchableOpacity, Button} from 'react-native';
import PropTypes from 'prop-types';
import { withNavigation } from 'react-navigation';
import { StackActions } from 'react-navigation';
class FixedHeader extends React.Component {
render() {
const popAction = StackActions.pop({
n: 0,
});
return (
<View style={FixedHeaderStyles.sectionHeader}>
<View style={FixedHeaderStyles.sectionHeaderTopLine}>
<View style={[FixedHeaderStyles.sectionHeaderBack, !(this.props.backButton) && FixedHeaderStyles.sectionHeaderHide ]}>
<TouchableOpacity
onPress={() => this.props.navigation.dispatch(popAction)}
>
<Text style={FixedHeaderStyles.sectionHeaderText}>< Back</Text>
</TouchableOpacity>
</View>
<View style={FixedHeaderStyles.logoBlock}>
<Image style={FixedHeaderStyles.homeBlockImg} source={require('../../images/logosmall.png')} />
</View>
<View style={FixedHeaderStyles.homeBlockBurger} >
<TouchableOpacity onPress={() => this.props.navigation.toggleDrawer() }>
<Image
style={FixedHeaderStyles.homeBurgerImg}
source={require('../../images/icons/ico-burger.png')}
/>
</ TouchableOpacity>
</View>
</View>
</View>
);
}
}
FixedHeader.propTypes = {
backButton: PropTypes.bool.isRequired,
navigate: PropTypes.object,
};
const FixedHeaderStyles = StyleSheet.create({
sectionHeadeLogo:{
width:45,
height:58,
alignSelf:'center'
},
sectionHeader:{
backgroundColor:'#1C2344',
flex:1.8,
alignSelf:'stretch',
borderBottomColor:'#f79431',
borderBottomWidth:1,
},
sectionHeaderTopLine:{
height:120,
paddingTop:45,
borderBottomColor:'#f79431',
borderBottomWidth:1,
flexDirection:'row',
justifyContent:'center',
alignItems:'center'
},
homeBlockBurger:{
position:'absolute',
right:0,
marginRight:15,
top:56
},
logoBlock:{
alignSelf:'flex-start',
},
homeBlockImg:{
width:45,
height:58,
alignSelf:'center',
},
homeBurgerImg:{
width:40,
height:40,
},
sectionHeaderHide:{
display:'none',
},
sectionHeaderBack:{
position:'absolute',
left:15,
top:70,
},
sectionHeaderText:{
color:'#fff',
},
});
export default withNavigation(FixedHeader);
The page navigates fine to the subview - but when pressing back the page jumps to the root (login page) rather than the previous page. For example - I navigate to rota from home, click back and jump back to login.. can anyone shine any light on this and point me in the correct direction - I've read the documentation but cant figure out whats going astray...
My Dependencies are as follows:
Here's how to do it in #react-navigation 6.x -we're in 2022 xD-
import { StackActions } from '#react-navigation/native';
const popAction = StackActions.pop(1);
navigation.dispatch(popAction);
documentation demo
https://reactnavigation.org/docs/stack-actions/#pop
snack demo https://snack.expo.dev/JEqNk9eBx
You shouldn't define your popAction with index to 0 in your FixedHeader class.
const popAction = StackActions.pop({
n: 0,
});
instead try
const popAction = StackActions.pop();
The pop action takes you back to a previous screen in the stack.
The n param allows you to specify how many screens to pop back
by.
Please refer to these docs: https://reactnavigation.org/docs/en/stack-actions.html#pop
Besides that, you would better define your const popAction = ... outside render() method.
import React from 'react';
import {View, Image, Text, StyleSheet, TouchableOpacity, Button} from 'react-native';
import PropTypes from 'prop-types';
import { withNavigation } from 'react-navigation';
import { StackActions } from 'react-navigation';
const popAction = StackActions.pop();
class FixedHeader extends React.Component {
render() {
return (
<View style={FixedHeaderStyles.sectionHeader}>
<View style={FixedHeaderStyles.sectionHeaderTopLine}>
<View style={[FixedHeaderStyles.sectionHeaderBack, !(this.props.backButton) && FixedHeaderStyles.sectionHeaderHide ]}>
<TouchableOpacity
onPress={() => this.props.navigation.dispatch(popAction)}
>
<Text style={FixedHeaderStyles.sectionHeaderText}>< Back</Text>
</TouchableOpacity>
</View>
<View style={FixedHeaderStyles.logoBlock}>
<Image style={FixedHeaderStyles.homeBlockImg} source={require('../../images/logosmall.png')} />
</View>
<View style={FixedHeaderStyles.homeBlockBurger} >
<TouchableOpacity onPress={() => this.props.navigation.toggleDrawer() }>
<Image
style={FixedHeaderStyles.homeBurgerImg}
source={require('../../images/icons/ico-burger.png')}
/>
</ TouchableOpacity>
</View>
</View>
</View>
);
}
}
FixedHeader.propTypes = {
backButton: PropTypes.bool.isRequired,
navigate: PropTypes.object,
};
const FixedHeaderStyles = StyleSheet.create({
sectionHeadeLogo:{
width:45,
height:58,
alignSelf:'center'
},
sectionHeader:{
backgroundColor:'#1C2344',
flex:1.8,
alignSelf:'stretch',
borderBottomColor:'#f79431',
borderBottomWidth:1,
},
sectionHeaderTopLine:{
height:120,
paddingTop:45,
borderBottomColor:'#f79431',
borderBottomWidth:1,
flexDirection:'row',
justifyContent:'center',
alignItems:'center'
},
homeBlockBurger:{
position:'absolute',
right:0,
marginRight:15,
top:56
},
logoBlock:{
alignSelf:'flex-start',
},
homeBlockImg:{
width:45,
height:58,
alignSelf:'center',
},
homeBurgerImg:{
width:40,
height:40,
},
sectionHeaderHide:{
display:'none',
},
sectionHeaderBack:{
position:'absolute',
left:15,
top:70,
},
sectionHeaderText:{
color:'#fff',
},
});
export default withNavigation(FixedHeader);
Cheers
Since you're using the withNavigation HOC, you'll want to use the navigation prop in your FixedHeader component instead of calling StackActions.pop. you can just call this.props.navigation.pop().
https://reactnavigation.org/docs/en/navigation-prop.html#navigator-dependent-functions
After a fair bit of head scratching and searching - I found an answer to my issue - My problem is that i'm using a drawer navigation rather than stack navigation - pop is not available to a drawer as there isnt an avilable stack -
ref - https://reactnavigation.org/docs/en/navigation-prop.html
answer from - https://github.com/react-navigation/react-navigation/issues/4793
The pop action takes you back to a previous screen in the stack. It takes one optional argument (count), which allows you to specify how many screens to pop back by.
import { StackActions } from '#react-navigation/native';
const popAction = StackActions.pop(1);
navigation.dispatch(popAction);

How do I launch the RN Expo Camera in fullscreen from TouchableOpacity press?

I have a React Native Component that is conditionally rendering a TouchableOpacity "Camera" Icon upon initial screen load, and pressing this TouchableOpacity "Camera" Icon is intended to launch the Expo's React Native Camera which is housed in a separate component. I implemented logging to track my state and confirmed that the state is correctly updating thus launching the Expo Camera component.
The problem I'm having is that NO MATTER WHAT I TRY, I cant get the camera Capture window to render in fullscreen and instead renders somewhere off of the iOS simulator app window. AND when the camera is activated, I cant see the capture window but it's messing up all of my containers once the camera loads.
CameraLauncher.js (conditionally shows either a Camera icon or shows the NativeCamera component view.
import React, { Component } from 'react';
import { StyleSheet, TouchableOpacity, Modal, Text, View } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import { Camera } from 'expo';
import { NativeCamera } from './NativeCamera';
export class CameraLauncher extends Component {
constructor(props) {
super(props);
this.setCameraShowing = this.setCameraShowing.bind(this);
this.state = {
type: Camera.Constants.Type.back,
cameraOpen: false
}
}
setCameraShowing(){
this.setState({cameraOpen: true});
console.log('hi', this.state.cameraOpen)
}
render() {
const visible = this.state.cameraOpen
console.log('ho', this.state.cameraOpen)
if (visible) {
camera =
<NativeCamera style={StyleSheet.absoluteFill} />
} else {
camera =
<TouchableOpacity style={styles.cameraBtn} onPress={() => this.setCameraShowing(true)}>
<View>
<Icon
name="camera"
size={25}
color='#2196F3'
/>
</View>
</TouchableOpacity>
}
return (
<View>
{camera}
</View>
);
}
}
const styles = StyleSheet.create({
cameraBtn: {
color: '#2196F3',
marginTop: -15
}
});
NativeCamera.js (the actual camera window)
import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Camera, Permissions } from 'expo';
export class NativeCamera extends Component {
constructor(props) {
super(props)
this.state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
};
}
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' });
}
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
} else {
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={this.state.type}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
this.setState({
type: this.state.type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back,
});
}}>
<Text
style={{ fontSize: 18, marginBottom: 10, color: 'white' }}>
{' '}Flip{' '}
</Text>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
}
}
How do I make the Expo Camera capture window load over the entire screen? I was able to accomplish this with a <Modal /> by using presentationStyle='overFullScreen, but I don't know how to make that happen with the camera.
Also, yes I have already looked over this a million times and haven't found any examples of others using a button to launch the camera. https://docs.expo.io/versions/latest/sdk/camera/

Text strings must be rendered within a <Text> component

Please what is wrong with this code.I have removed all white spaces. I have checked for semi colons in the code. I still have the same error.
I read before that it could happen because of white empty spaces but I don't see any empty white spaces in my code below. I have edited the question.
import React, { Component } from 'react';
import { StyleSheet, ScrollView, View } from 'react-native';
import { List, ListItem, Text, Card } from 'react-native-elements';
class DetailsScreen extends Component {
static navigationOptions = {
title: 'Details',
};
render() {
const { navigation } = this.props;
const matches = JSON.parse(navigation.getParam('matches', 'No matches found'));
console.log(matches)
return (
<ScrollView>
<Card style={styles.container}>
{
matches.map((item, key) => (
<View key={key} style={styles.subContainer}>
<View>
<Text style={styles.baseText}>{item.group}</Text>
</View>
<View>
<Text style={styles.baseText}>{item.team1.name}</Text>
<Text>{item.team2.name}</Text>
</View>
<View>
<Text style={styles.baseText}>{item.date}</Text>
</View>
<View>
<Text style={styles.baseText}>{item.score1}</Text>
<Text>{item.score2}</Text>
</View>
if(item.goals1.length > 0) {
item.goals1.map((item2, key2) => (
<View key={key2}><Text style={styles.baseText}>{item2.name} {item2.minute}</Text></View>
))
}
if(item.goals2.length > 0) {
item.goals2.map((item3, key3) => (
<View key={key3}><Text style={styles.baseText}>{item3.name} {item3.minute}</Text></View>
))
}</View>
))
}
</Card>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20
},
subContainer: {
flex: 1,
paddingBottom: 20,
borderBottomWidth: 2,
borderBottomColor: '#CCCCCC'
},
baseText:{
fontFamily:'Cochin',
fontSize:14,
fontWeight:'bold'}
})
export default DetailsScreen;
remove h2, h3, h3 from Text components since it has no props (not fatal, but useless) and you dont need () here either ({item2.minute}) which actually breaks the code
i suggest also checking out what props and methods Text component have here:
https://facebook.github.io/react-native/docs/text.html
hope this helps
Agree with wijuwiju's answer also do not nest other elements including <Text> inside a <Text> component. It will mostly create problems even if now on one platform you don't see any problems.

How to add stateless component to a touchable component

I am trying to add a stateless component to my button.
const button = ({onButtonPress, buttonText}) => {
return (
<TouchableHighlight
onPress={() => onButtonPress()}>
<ButtonContent text={buttonText}/>
</TouchableHighlight>
)
};
and get this error:
Warning: Stateless function components cannot be given refs (See ref "childRef"
in StatelessComponent created by TouchableHighlight).
Attempts to access this ref will fail.
I have read up on the issue but I am still new to javascript and RN and have not found a solution. Any help would be appreciated.
full code:
GlossaryButtonContent:
import React from 'react';
import {
View,
Text,
Image,
StyleSheet
} from 'react-native';
import Colours from '../constants/Colours';
import {
arrowForwardDark,
starDarkFill
} from '../assets/icons';
type Props = {
text: string,
showFavButton?: boolean
}
export default ({text, showFavButton} : Props) => {
return (
<View style={styles.container}>
{showFavButton &&
<Image
style={styles.star}
source={starDarkFill}/>}
<Text style={[styles.text, showFavButton && styles.favButton]}>
{showFavButton ? 'Favourites' : text}
</Text>
<Image
style={styles.image}
source={arrowForwardDark}
opacity={showFavButton ? .5 : 1}/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
},
favButton: {
marginLeft: 10,
color: Colours.darkTextHalf
},
text: {
flex: 1,
paddingTop: 5,
marginLeft: 20,
fontFamily: 'Bariol-Bold',
fontSize: 24,
color: Colours.darkText
},
image: {
marginRight: 20
},
star: {
marginLeft: 10
}
});
GlossaryButton:
import React from 'react';
import {
TouchableHighlight,
StyleSheet
} from 'react-native';
import Colours from '../constants/Colours';
import ShadowedBox from './ShadowedBox';
import GlossaryButtonContent from './GlossaryButtonContent';
type Props = {
buttonText: string,
onButtonPress: Function,
rowID: number,
sectionID?: string,
showFavButton?: boolean
}
export default ({buttonText, onButtonPress, rowID, sectionID, showFavButton} : Props) => {
return (
<ShadowedBox
style={styles.container}
backColor={showFavButton && Colours.yellow}>
<TouchableHighlight
style={styles.button}
underlayColor={Colours.green}
onPress={() => onButtonPress(rowID, sectionID)}>
<GlossaryButtonContent
text={buttonText}
showFavButton={showFavButton}/>
</TouchableHighlight>
</ShadowedBox>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
height: 60,
marginBottom: 10,
borderRadius: 5
},
button: {
flex: 1,
borderRadius: 5
}
});
Basically, Stateless components cannot have refs.
So, having a stateless component in the middle of the render tree will create a void in the ref chain, meaning you cannot access lower-down components.
So, the problem comes from trying to do this:
let Stateless = (props) => (
<div />
);
let Wrapper = React.createClass({
render() {
return <Stateless ref="stateeee" />
}
});
TouchableHighlight needs to give a ref to its child. And this triggers that warning.
Answer:
You can't actually make a stateless component a child of TouchableHighlight
Solution:
Use createClass or class to create the child of TouchableHighlight, that is GlossaryButtonContent.
See this github issue for more info and this one

React Native custom event

Just starting out with React Native, and was curious on how best to achieve passing events to a parent component from children buttons. So for example:
import React, { Component } from 'react';
import ViewContainer from '../components/ViewContainer';
import SectionHeader from '../components/SectionHeader';
import {
View
} from 'react-native';
class App extends Component {
render() {
return (
<ViewContainer>
<SectionHeader onCreateNew = {() => console.log("SUCCESS")} ></SectionHeader>
</ViewContainer>
);
}
}
module.exports = ProjectListScreen;
and here is my section header. I was attempting to use an Event Emitter, but not working the way I'd hoped, unless I missed something.
import React, { Component } from 'react';
import Icon from 'react-native-vector-icons/FontAwesome';
import EventEmitter from "EventEmitter"
import {
StyleSheet,
TouchableOpacity,
Text,
View
} from 'react-native';
class SectionHeader extends Component {
componentWillMount() {
console.log("mounted");
this.eventEmitter = new EventEmitter();
this.eventEmitter.addListener('onCreateNew', function(){
console.log('myEventName has been triggered');
});
}
_navigateAdd() {
this.eventEmitter.emit('onCreateNew', { someArg: 'argValue' });
}
_navigateBack() {
console.log("Back");
}
render() {
return (
<View style={[styles.header, this.props.style || {}]}>
<TouchableOpacity style={{position:"absolute", left:20}} onPress={() => this._navigateBack()}>
<Icon name="chevron-left" size={20} style={{color:"white"}} />
</TouchableOpacity>
<TouchableOpacity style={{position:"absolute", right:20}} onPress={() => this._navigateAdd()}>
<Icon name="plus" size={20} style={{color:"white"}} />
</TouchableOpacity>
<Text style={styles.headerText}>Section Header</Text>
</View>
);
}
}
const styles = StyleSheet.create({
header: {
height: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#00B9E6',
flexDirection: 'column',
//paddingTop: 25
},
headerText: {
fontWeight: 'bold',
fontSize: 20,
color: 'white'
},
});
module.exports = SectionHeader;
Thanks for your help!
Not sure if this is the right way to go about this, but using this.props.onCustomEvent from the nested Button's onPress event seems to work fine. Please let me know if there is a better way to go about doing this.
App:
import React, { Component } from 'react';
import ViewContainer from '../components/ViewContainer';
import SectionHeader from '../components/SectionHeader';
import {
View
} from 'react-native';
class App extends Component {
render() {
return (
<ViewContainer>
<SectionHeader onCreateNew = {() => console.log("SUCCESS")} ></SectionHeader>
</ViewContainer>
);
}
}
module.exports = ProjectListScreen;
Section Header
import React, { Component } from 'react';
import Icon from 'react-native-vector-icons/FontAwesome';
import {
StyleSheet,
TouchableOpacity,
Text,
View
} from 'react-native';
class SectionHeader extends Component {
render() {
return (
<View style={[styles.header, this.props.style || {}]}>
<TouchableOpacity style={{position:"absolute", left:20}} onPress={() => this.props.onCreateNew()}>
<Icon name="chevron-left" size={20} style={{color:"white"}} />
</TouchableOpacity>
<Text style={styles.headerText}>Section Header</Text>
</View>
);
}
}
const styles = StyleSheet.create({
header: {
height: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#00B9E6',
flexDirection: 'column',
//paddingTop: 25
},
headerText: {
fontWeight: 'bold',
fontSize: 20,
color: 'white'
},
});
module.exports = SectionHeader;
In addition to #arbitez's answer, if you want to maintain scope then you should make a method and bind to it, eg:
Parent:
constructor(props) {
super(props)
// ........
this.onCreateNew=this.onCreateNew.bind(this);
}
onCreateNew() {
console.log('this: ', this);
}
render() {
return (
<ViewContainer>
<SectionHeader onCreateNew = {this.onCreateNew} ></SectionHeader>
</ViewContainer>
);

Resources