0
votes

"objects are not valid as a react child (found: object with keys {date, events}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of View."

So I have kind of a cascade of method calls. I'm retrieving dates with events inside of those. It feels like I'm doing this correctly, but am getting the above error. I've tried setting createFragment on places, but still getting the error. Here's the code:

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

Thanks!

EDIT: Updated code per the map suggestion, still not working. complaining about {events} only now.

EDIT 2: Updated with FULL code.

1

1 Answers

1
votes

The component's render helpers, such as renderDates(), are returning _.each(...). _.each() returns its first argument so this is why you are receiving the error.

To illustrate:

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

I recommend you use Array.prototype.map() instead:

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

If you use arrow functions like I did in the example above, there's no need to save a reference to this. this in the body of the function passed to map() will be bound to the instance of the component. You can then call other helper methods such as getImageStyles() like this.getImageStyles(...).

This is not related to your original question but the getApiData() method will not work. You can replace the function in the chain that handles responseJson with something like:

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

You also need to to remove the this.state = {...} in componentDidMount(). Notice the warning in the docs that indicates you should "NEVER mutate this.state directly".