2016-09-05 4 views
0

«Объекты недействительны в качестве реагирующего ребенка (найдено: объект с ключами {дата, события}). Если вы хотели отобразить коллекцию детей, используйте вместо массива или обернуть объект, используя createFragment (объект) из надстроек React. Проверьте метод рендеринга 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); 

Спасибо!

EDIT: Обновленный код на предложение карты, все еще не работает. жалуясь только на {events}.

EDIT 2: Обновлен ПОЛНЫМ кодом.

ответ

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 напрямую».

+0

Отредактировано выше с новым кодом, такой же проблема. Теперь он просто делает это для {events}. – charliepage88

+0

Вы уверены, что хотите опубликовать полный код? 'getApiData()' возвращает 'undefined'. Я также не уверен, что такое 'createFragement()', но здесь это не обязательно. Наконец, вы должны удалить 'this.state = {...}' в 'componentDidMount()'. –

+0

Не полный код, подумал, что этого будет достаточно ... но я разместил содержимое моего файла index.ios.js – charliepage88

Смежные вопросы