Reputation: 53
I'm having trouble learning how to pass data between parent and child in React Native.
In my parent component I have a state property (audioPlaying) which is a Boolean value.
state = {
//The title informs the Button and TitleArea components
title: 'hello',
audioPlaying: false,
};
I'd like to change that value on the press of a button (onPress).
<Button
title={this.state.title}
onPress={this.playPauseHandler}
audioPlaying={this.state.audioPlaying}
/>
...by calling the playPauseHandler.
playPauseHandler = () => {
this.setState(prevState => ({
audioPlaying: !prevState.audioPlaying
}));
}
Then in my child (Button) Component I want to evaluate the audioPlaying state property. If it's true, I want to show one things and false I want to show something else.
<View style={styles.playBtnStyle}>
{this.props.audioPlaying === false ? (
<MaterialIcons
name='play-arrow'
size={50}
color="#87888C"
/>
) : (
<MaterialIcons
name='pause'
size={50}
color="#87888C"
/>
)}
}
</View>
However, when I run this I get undefined for the value of audioPlaying. React Native Error Message
Here are the full files for both:
App.js
import React, { Component } from 'react';
import { View, StatusBar } from 'react-native';
import Carousel from './src/components/Carousel/Carousel';
import Button from './src/components/Button/Button';
import TitleArea from './src/components/TitleArea/TitleArea';
import MapArea from './src/components/MapArea/MapArea';
const styles = {
container: {
flex: 1,
justifyContent: 'space-between',
},
playArea: {
flex: 1,
},
};
export default class App extends Component {
state = {
//The title informs the Button and TitleArea components
title: 'hello',
audioPlaying: false,
};
playPauseHandler = () => {
this.setState(prevState => ({
audioPlaying: !prevState.audioPlaying
}));
}
render() {
return (
<View style={styles.container}>
<TitleArea title={this.state.title} />
<StatusBar hidden={false} />
<Carousel />
<MapArea />
<Button
title={this.state.title}
onPress={this.playPauseHandler}
audioPlaying={this.state.audioPlaying}
/>
</View>
);
}
}
Button.js
import React, { Component } from 'react';
import { Text, View, TouchableOpacity, Dimensions } from 'react-native';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
const { width } = Dimensions.get('window');
const height = width * 0.2;
const styles = {
textStyle: {
color: '#87888C',
fontSize: 18,
fontWeight: '600',
backgroundColor: 'white',
alignSelf: 'center',
},
buttonContainer: {
height,
flexDirection: 'row',
backgroundColor: 'white',
alignItems: 'center',
},
playBtnStyle: {
marginLeft: 50,
backgroundColor: 'white',
},
childStyle: {
flex: 1,
},
};
const button = (props) => {
return (
<View style={styles.buttonContainer}>
<TouchableOpacity>
<View style={styles.playBtnStyle}>
{this.props.audioPlaying === false ? (
<MaterialIcons
name='play-arrow'
size={50}
color="#87888C"
/>
) : (
<MaterialIcons
name='pause'
size={50}
color="#87888C"
/>
)}
}
</View>
</TouchableOpacity>
<View style={styles.childStyle}>
<Text style={styles.textStyle}>Chapter 1: {props.title}</Text>
</View>
</View>
);
}
export default button;
Upvotes: 0
Views: 2573
Reputation: 53
Ok so I solved my own problem! (step one to being a developer)
Two issues:
Capturing Touch Events
React Native has what's called Touchables. According to the documentation these are "wrappers that make views respond properly to touches".
TouchableOpacity, the one I'm using:
On press down, the opacity of the wrapped view is decreased, dimming it. Opacity is controlled by wrapping the children in an Animated.View, which is added to the view hierarchy.
https://facebook.github.io/react-native/docs/touchablewithoutfeedback#onpress
All Touchables accept the onPress prop. So by adding the onPress prop to the Touchable, I'm able to capture the touch event instead of just firing it.
Passing Callback to Parent
This article helped me understand more about how a parent function can be called from a child.
https://medium.com/@thejasonfile/callback-functions-in-react-e822ebede766
So I'm calling playPause() (I renamed the prop and destructured it) in TouchableOpacity, which fires from a touch event causing state to change and component to re-render.
const button = (props) => {
const {
title,
audioPlaying,
playPause,
} = props;
return (
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() => playPause()}>
<View style={styles.playBtnStyle}>
{audioPlaying === false ? (
<MaterialIcons
name='play-arrow'
size={50}
color="#87888C"
/>
) : (
<MaterialIcons
name='pause'
size={50}
color="#87888C"
/>
)
}
</View>
</TouchableOpacity>
<View style={styles.childStyle}>
<Text style={styles.textStyle}>
Chapter 1:
{title}
</Text>
</View>
</View>
);
};
Upvotes: 0
Reputation: 371
There is no this
in the context of button. That is just a function returning JSX.
Instead, use props
<View style={styles.playBtnStyle}>
{props.audioPlaying === false ? (
<MaterialIcons
name='play-arrow'
size={50}
color="#87888C"
/>
) : (
<MaterialIcons
name='pause'
size={50}
color="#87888C"
/>
)}
</View>
Upvotes: 4