【问题标题】:React Native - Conditional Styles on Parent Based on Child StateReact Native - 基于子状态的父级条件样式
【发布时间】:2016-04-25 04:40:52
【问题描述】:

我正在关注 React Native 教程,并尝试对其进行调整以显示歌曲列表而不是电影,并使用 Switch 组件添加切换功能。

我设法让它工作,但现在我试图将开关的值发送回父级,以便可以应用条件样式。

当我尝试这样做时,我收到一条错误消息

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

这似乎是明智的,因为 togglePlayed 中的 console 语句似乎从未被调用过。

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

任何指针都会很棒,因为我是 React 新手,尤其是 React Native。

【问题讨论】:

  • 您应该删除样式和歌曲以使其更易于阅读:P
  • 是的,显然这些歌曲不是真实数据,我将其截断为两个虚构的示例以尽量简洁。我可以删除它们,我只是认为包含它们会更好。
  • 我猜你的问题是你没有绑定togglePlayed到你的组件,你忘了在构造函数中绑定它,至少它在React中是这样工作的(我不知道React native寿!)
  • @QoP 我注意到在阅读有关将状态从子组件传递到父组件的其他问题时使用了bind,但我看到的一个示例没有使用它,所以当它使用时我有点困惑需要,什么时候不需要。如果您有解决方案,如果您有时间发布,我很乐意将其标记为已接受?

标签: reactjs react-native


【解决方案1】:

你忘记将你的函数绑定到你的组件,它应该是这样的

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

【讨论】:

  • 非常感谢@QoP,不过似乎是同样的错误(rnplay.org/apps/mnanvw)。如果条件样式被去掉了,那么从togglePlayed 的控制台日志似乎仍然没有到达,你有什么想法吗?。
  • 我写的评论没有意义,我没有看到你使用的是ES5语法,无论如何都尝试用ES6语法编写它们
  • hmm,对于这个我不确定,这就是为什么我将getInitialState 放在开关组件中,以便将played 初始化为false。你能用你的建议更新答案吗?在此期间我会尝试一下。
  • 我添加了 BasicSwitch 组件
  • 非常感谢。我现在得到的错误是'this' is not allowed before super()。刚才看一下,看看我能不能找出问题所在。
猜你喜欢
  • 2017-02-25
  • 1970-01-01
  • 2018-08-19
  • 2020-09-09
  • 1970-01-01
  • 1970-01-01
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多