简体   繁体   中英

Read the value of Parent state and pass Boolean to Child Component React

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; 

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>

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> ); }; 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM