错误"对象无效作为反应孩子"反应原生

时间:2016-09-05 01:10:52

标签: react-native

"对象作为反应子对象无效(找到:具有键{date,events}的对象)。如果您要渲染子集合,请使用数组,或使用React附加组件中的createFragment(object)包装对象。检查View的渲染方法。"

所以我有一种方法调用的级联。我正在检索那些内部事件的日期。感觉就像我正确地做到了这一点,但我得到了上述错误。我尝试在地方设置createFragment,但仍然收到错误。这是代码:

  import React, { Component } from 'react';
  import {
    AppRegistry,
    Text,
    View,
    ScrollView,
    RefreshControl,
    StyleSheet,
    Dimensions,
    TextInput,
    Linking,
    TouchableNativeFeedback
   } from 'react-native';

  var _ = require('lodash');
  var {width, height} = Dimensions.get('window');
  var renderif = require('render-if');
  var createFragment = require('react-addons-create-fragment');

  var IMAGES_PER_ROW = 1

  class FunInATL extends Component {
    constructor(props) {
      super(props);
      this.state = {
        currentScreenWidth: width,
        currentScreenHeight: height,
        dates: [],
        boxIndex: 0,
        showBox: false,
        refreshing: false
      };
    }

        handleRotation(event) {
          if (!this.state) {
            return;
          }

          var layout = event.nativeEvent.layout
          this.state({currentScreenWidth: layout.width, currentScreenHeight: layout.height })
        }

        calculatedSize() {
          var size = this.state.currentScreenWidth / IMAGES_PER_ROW
          return {width: size}
        }

        renderRow(events) {
          return events.map((events, i) => {
            return (
              <Image key={i} style={[this.getImageStyles(), styles.image, this.calculatedSize() ]} source={{uri: event.image}} />
            )
          })
        }

        openUrl(url) {
          Linking.canOpenURL(url).then(supported => {
              if (supported) {
                Linking.openURL(url);
              } else {
                console.log('nope :: ' + url);
              }
          }).catch(err => console.error('An error occurred', err));
        }

        getImageStyles(featured, category) {
            let options = {
              borderColor: 'gold',
              borderWidth: featured ? 1 : 0
            }

            if (!category) {
                options.height = featured ? 250 : 125
            }

            return options;
        }

        _clickImage(event, index) {
          if (event.title) {
            let new_val = !this.state.showBox
            this.setState({
                dates: this.state.dates,
                showBox: new_val,
                boxIndex: new_val ? index : 0
            });
          }

        }

        componentDidMount() {
          this.state = {
              dates: [],
              boxIndex: 0,
              showBox: false,
              refreshing: false
          };
          this.getApiData();

          Linking.addEventListener('url', this.handleUrl);
        }

        componentWillUnmount() {
            Linking.removeEventListener('url', this.handleUrl);
        }

        getApiData() {
          var _this = this;
          return fetch('https://www.funinatl.com/mobile2.php?v1')
            .then(function(response) {
              return response.json()
            })
            .then((responseJson) => {
              var dates = createFragment(responseJson.events)
              return;

              let _this = this;

              date.events.map((event, i) => (
                date.events[i] = event
              ))

              var datesData = [];
              dates.map((date, i) => (
                datesData.push({
                  date: i,
                  events: createFragment(date.events)
                })
              ))

              _this.setState({
                dates: createFragment(datesData),
                boxIndex: 0,
                showBox: false
              })

              console.error(this.state);
            })
            .catch((error) => {
              console.error(error);
            })
            .done();
        }

        renderDates() {
          return this.state.dates.map((date) =>
                (
                  <View>
                    <Text style={styles.dateHeader}>{ date.date }</Text>

                    <View>
                    {this.renderEvents(date.events)}
                    </View>
                  </View>

            ))
        }

        renderImage(event, index) {
            if (this.state.showBox && this.state.boxIndex == index) {
              return (
                <View>
                  <TouchableNativeFeedback onPress={()=>this._clickImage(event, index)}>
                      <Image source={{ uri: event.image }} style={[styles.image, this.calculatedSize(), this.getImageStyles(event.featured), { height: 100 }]} />
                  </TouchableNativeFeedback>
                  <View style={{ flexDirection:'row', padding: 15 }}>
                      <Text style={styles.price}>{event.price}</Text>
                      <Text style={styles.time}>{event.time}</Text>
                      <TouchableNativeFeedback onPress={()=>this.openUrl(event.website)}>
                          <Text style={styles.btn}>Website</Text>
                      </TouchableNativeFeedback>
                  </View>

                      {renderif(event.venue)(
                          <TouchableNativeFeedback onPress={()=>this.openUrl(event.venue)}>
                              <Text style={styles.btn}>Venue</Text>
                          </TouchableNativeFeedback>
                      )}

                </View>
              )
            } else {
              return (
                <View>
                    <TouchableNativeFeedback onPress={()=>this._clickImage(event, index)}>
                        <Image source={{ uri: event.image }} style={[styles.image, this.calculatedSize(), this.getImageStyles(event.featured)]} />
                    </TouchableNativeFeedback>
                </View>
              )
            }
        }

        renderEvents(events) {
          return events.map((event, i) =>
            (
              <View>
                <Text style={[styles.eventCategory, this.getImageStyles(event.featured, true)]}>{event.category}</Text>
                <View>
                  {this.renderImage(event, i)}
                </View>
                <Text style={[styles.eventTitle, this.getImageStyles(event.featured, true)]}>{event.title}</Text>
              </View>
          ));
        }

        _onRefresh() {
          this.setState({refreshing: true});
          fetchData().then(() => {
            this.setState({refreshing: false});
          });
        }

        render() {
          return (
            <ScrollView onLayout={this.handleRotation} contentContainerStyle={styles.scrollView} refreshControl={
                <RefreshControl
                  refreshing={this.state.refreshing}
                  onRefresh={this._onRefresh.bind(this)}
                  tintColor="#ff0000"
                  title="Loading..."
                  titleColor="#00ff00"
                  colors={['#ff0000', '#00ff00', '#0000ff']}
                  progressBackgroundColor="#ffff00"
                />
              }>
              <Text style={styles.header}>FunInATL</Text>
                  {this.renderDates()}
            </ScrollView>
          )
        }
      }

  var styles = StyleSheet.create({

    row: {
      flexDirection: 'row',
      alignItems: 'center',
      justifyContent: 'flex-start',
      textAlign: 'center',
      padding: 10
    },

    header: {
      fontSize: 30,
      fontWeight: 'bold',
      padding: 20,
      textAlign: 'center',
      backgroundColor: '#000',
      color: '#fff'
    },

    dateHeader: {
      fontSize: 20,
      fontWeight: 'bold',
      padding: 20,
      textAlign: 'left',
      color: '#fff',
      backgroundColor: '#283593'
    },

    eventCategory: {
        backgroundColor: '#03a9f4',
        textAlign: 'center',
        color: '#ffffff',
        padding: 3
    },

    eventTitle: {
        borderTopWidth: 0,
        textAlign: 'center',
        fontWeight: 'bold',
        padding: 3,
        fontSize: 18,
    },

    image: {
    },

    btn: {
        backgroundColor: 'green',
        padding: 10,
        color: '#fff',
        textAlign: 'center',
        flex: 1
    },

    price: {
        marginLeft: 10,
        fontSize: 16,
        flex: 1
    },

    time: {
      marginRight: 10,
      fontSize: 16,
      flex: 1,
      width: 35
    }

  });

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

谢谢!

编辑:根据地图建议更新了代码,但仍无效。现在只抱怨{events}。

编辑2 :已更新为完整代码。

1 个答案:

答案 0 :(得分:1)

组件的渲染助手(例如renderDates())正在返回_.each(...)_.each()返回其第一个参数,因此这就是您收到错误的原因。

举例说明:

const myObject = { a: 1 };
_.each(myObject) === myObject // true

我建议您改为使用Array.prototype.map()

return this.state.dates.map((date) => (
  <View>...</View>        
));

如果您像上面的示例中那样使用arrow functions,则无需保存对this的引用。传递给this的函数体中的map()将绑定到组件的实例。然后,您可以调用getImageStyles()之类的其他辅助方法,例如this.getImageStyles(...)

这与您的原始问题无关,但getApiData()方法不起作用。您可以使用以下内容替换处理responseJson的链中的函数:

(responseJson) => {
  this.setState({
    dates: Object.entries(responseJson.events).map(([date, { events }]) => ({
      date,
      events,
    })),
    boxIndex: 0,
    showBox: false,
  });
}

您还需要删除this.state = {...}中的componentDidMount()。注意warning in the docs表示你应该“永远不要改变this.state”。