martincarlin87
martincarlin87

Reputation: 11042

React Native - Conditional Styles on Parent Based on Child State

I was following the React Native tutorial and have tried to adapt it to show a list of songs rather than movies and add in a toggling ability using the Switch component.

I managed to get this to work but now I am trying to send the value of the switch back to the parent so that a conditional style can be applied.

When I attempted to do this, I get an error saying

undefined is not an object (evaluating 'this.state.played')

which seems sensible since the console statement in the togglePlayed never seems to be called.

import React, {
  AppRegistry,
  Component,
  Image,
  ListView,
  StyleSheet,
  Text,
  View,
  Switch
} from 'react-native';

var SONGS_DATA = {
  "songs" : [
    {
      "title" : "I Heard React Was Good",
      "artist" : "Martin",
      "played" : false
    },
    {
      "title" : "Stack Overflow",
      "artist" : "Martin",
      "played" : false
    }
  ]
}

var BasicSwitchExample = React.createClass({
  getInitialState() {
    return {
      played: false
    };
  },
  handlePlayed(value) {
    console.log('Switch has been toggled, new value is : ' + value)
    this.setState({played: value})
    this.props.callbackParent(value);
  },
  render() {
    return (
      <View> 
        <Switch
          onValueChange={this.handlePlayed}
          style={{marginBottom: 10}}
          value={this.state.played} />
      </View>
    );
  }
});

class AwesomeProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      loaded: false,
    };
  }

  componentDidMount() {
    this.fetchData();
  }

  getInitialState() {
    return {
      played: false
    };
  }

  togglePlayed(value) {
    // this is never reached
    this.setState({played: value});
    console.log('Song has been played? ' + this.state.played);
  }

  fetchData() {
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(SONGS_DATA.songs),
      loaded: true,
    });
  }

  render() {
    if (!this.state.loaded) {
      return this.renderLoadingView();
    }

    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderSong}
        style={styles.listView}
      />
    );
  }

  renderLoadingView() {
    return (
      <View style={styles.container}>
        <Text>
          Loading songs...
        </Text>
      </View>
    );
  }

  renderSong(song) {
    return (
      // not sure if this syntax is correct
      <View style={this.state.played ? 'styles.container' : 'styles.played'}>
        <View style={styles.half}>
          <Text style={styles.title}>{song.title}</Text>
          <Text style={styles.artist}>{song.artist}</Text> 
        </View>
        <View style={styles.half}>
          <BasicSwitchExample callbackParent={() => this.togglePlayed} />
        </View>
      </View>
    );
  }
}

var styles = StyleSheet.create({
  /* styles here */
});

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);

React Native Playground

Any pointers would be great as I am new to React and especially React Native.

Upvotes: 1

Views: 1654

Answers (1)

QoP
QoP

Reputation: 28397

You forgot to bind your function to your component, it should look like this

class BasicSwitchExample extends Component{
    constructor(props){
      super(props);
      this.state = {
         played: false
       };
      this.handlePlayed = this.handlePlayed.bind(this);
    }
    handlePlayed(value){
        this.setState({played: value});
        this.props.callbackParent(value);
    }
    render() {
      return <View> 
        <Switch
          onValueChange={this.handlePlayed}
          style={{marginBottom: 10}}
          value={this.state.played} />
      </View>
  }
}
class AwesomeProject extends Component {
  constructor(props) {
    super(props);
    this.renderSong = this.renderSong.bind(this);
    this.togglePlayed = this.togglePlayed.bind(this); 
    this.fetchData = this.fetchData.bind(this);
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      loaded: false,
    };
  }

  componentDidMount() {
    this.fetchData();
  }

  togglePlayed(value) {
    // this is never reached
    this.setState({played: value});
    console.log('Song has been played? ' + this.state.played);
  }

  fetchData() {
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(SONGS_DATA.songs),
      loaded: true,
    });
  }

  render() {
    if (!this.state.loaded) {
      return this.renderLoadingView();
    }

    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderSong}
        style={styles.listView}
      />
    );
  }

  renderLoadingView() {
    return (
      <View style={styles.container}>
        <Text>
          Loading songs...
        </Text>
      </View>
    );
  }

  renderSong(song) {
    return (
      // not sure if this syntax is correct
      <View style={this.state.played ? 'styles.container' : 'styles.played'}>
        <View style={styles.half}>
          <Text style={styles.title}>{song.title}</Text>
          <Text style={styles.artist}>{song.artist}</Text> 
        </View>
        <View style={styles.half}>
          <BasicSwitchExample callbackParent={this.togglePlayed} />
        </View>
      </View>
    );
  }
}

Upvotes: 1

Related Questions