列出 geofire 区域内来自 firebase 的元素

List elements from firebase within geofire region

我正在尝试显示落在当前位置地理半径范围内的商家列表。我遇到的问题是数据源似乎没有填充任何行。

import React from 'react'
import { View, ListView, Image, Text, ActivityIndicator } from 'react-native'
import ParallaxScrollView from 'react-native-parallax-scroll-view'
import AlertMessage from '../Components/AlertMessage'
import Geocoder from 'react-native-geocoder'
import { Colors } from '../Themes/'
import { connect } from 'react-redux'
import MerchantRow from '../Components/MerchantRow'
import { firebaseApp } from '../Config/AppConfig'
const GeoFire = require('geofire')

// Styles
import styles from './Styles/MerchantListStyle'

class MerchantList extends React.Component {

  watchID: ?number = null;

  constructor (props) {
    super(props)
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2
      }),
      result: '',
      city: 'you'
    }
    // Get references to several nodes we will need
    rootRef = firebaseApp.database().ref();
    merchantsRef = rootRef.child('merchants');
    geofireRef = rootRef.child('locations');
    this.geoFire = new GeoFire(geofireRef)
  }

  componentDidMount () {
    navigator.geolocation.getCurrentPosition(
      (position) => {
      },
      (error) => alert(JSON.stringify(error)),
      {enableHighAccuracy: true, timeout: 20000, maximumAge: 1000}
    )

    this.watchID = navigator.geolocation.watchPosition((position) => {
      var lat = position.coords.latitude;
      var lng = position.coords.longitude;

      Geocoder.fallbackToGoogle('AIzaSyCPVaJK7Sx-Uv6X76508NUlrpF3YJBqgrk')

      let ret = Geocoder.geocodePosition({lat, lng}).then((res)=>
      {
        city = res['0'].locality
        this.setState({ city: city })
      })

      let radius = 40 // 40km
      let currentLocation = [
        position.coords.latitude,
        position.coords.longitude
      ]

      let geoQuery = this.geoFire.query({center: currentLocation, radius})
      var merchants = [];
      // For every key which matches our GeoQuery...
      geoQuery.on('key_entered', function(key) {
        // ... look up the merchant data for that key ...
        // console.log(key)
        merchantsRef.child(key).once('value').then(function(snapshot) {
          // ... and add it to the matches array
          merchants.push(snapshot.val())
          console.log(merchants)
          this.setState({
            loading: false,
            dataSource: this.state.dataSource.cloneWithRows(merchants)
          })
        }).catch(function(error) {
          console.log('Error fetching merchant data:', error)
        })
      })
    })
  }

  componentWillUnmount () {
    navigator.geolocation.clearWatch(this.watchID)
  }

  _renderRow (item) {
    return (
      <MerchantRow data={item} />
    )
  }

  _noRowData () {
    return this.state.dataSource.getRowCount() === 0
  }

  render () {
    return (
      <View style={styles.container}>
        <ListView
          dataSource={this.state.dataSource}
          renderRow={this._renderRow.bind(this)}
          enableEmptySections
          pageSize={15}
          renderScrollComponent={props => (
            <ParallaxScrollView
              backgroundColor={Colors.background}
              contentBackgroundColor={Colors.background}
              parallaxHeaderHeight={250}
              renderBackground={() => (
                <Image
                  style={styles.photo}
                  source={{uri: this.props.photo}}
                  defaultSource={require('../Images/Merchant_Header.png')}
                />
              )}
              renderForeground={() => (
                <View key='parallax-header' style={styles.parallaxHeader}>
                  <Text style={styles.title}>
                    {'Choose a café near\n' + this.state.city + ' to start\n your order'}
                  </Text>
                </View>
              )}
            />
          )}
        />
        <AlertMessage title='Nothing to See Here, Move Along' show={this._noRowData()} />
      </View>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    // ...redux state to props here
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(MerchantList)

出现此问题是因为您使用的是普通回调函数,而不是使用使用粗箭头的 ES6 函数。

  geoQuery.on('key_entered', function(key) {
    // ... look up the merchant data for that key ...
    // console.log(key)
    merchantsRef.child(key).once('value').then(function(snapshot) {
      // ... and add it to the matches array
      merchants.push(snapshot.val())
      console.log(merchants)
      this.setState({
        loading: false,
        dataSource: this.state.dataSource.cloneWithRows(merchants)
      })
    }).catch(function(error) {
      console.log('Error fetching merchant data:', error)
    })
  })

this 对象应该引用 this.state.dataSource.cloneWithRows(merchants) 中的 class。但是,在 Javascript 中,正常回调 function() {} 将创建并绑定自己的 this 对象,而不是来自其父对象的对象,这意味着 this.state.dataSource.cloneWithRows(merchants) 现在无效。为防止这种情况,您应该使用不执行任何 this 绑定的 ES6 函数语法。

您更新后的代码应如下所示:

  geoQuery.on('key_entered', (key) => { // Using fat arrows
    // ... look up the merchant data for that key ...
    // console.log(key)
    merchantsRef.child(key).once('value').then((snapshot) => { // here too!
      // ... and add it to the matches array
      merchants.push(snapshot.val())
      console.log(merchants)
      this.setState({
        loading: false,
        dataSource: this.state.dataSource.cloneWithRows(merchants)
      })
    }).catch((error) => { // If you are planning to use this here
      console.log('Error fetching merchant data:', error)
    })
  })

如果你想使用普通的回调函数语法而不是ES6版本,你可以在函数外创建一个对此的引用,并在函数内使用新的引用。在这种情况下,您的代码应如下所示:

const that = this; // created a reference!      
     geoQuery.on('key_entered', function(key) {
        // ... look up the merchant data for that key ...
        // console.log(key)
        merchantsRef.child(key).once('value').then(function(snapshot) {
          // ... and add it to the matches array
          merchants.push(snapshot.val())
          console.log(merchants)
          that.setState({ // Use the reference here!
            loading: false,
            dataSource: that.state.dataSource.cloneWithRows(merchants) // Here too!
          })
        }).catch(function(error) {
          console.log('Error fetching merchant data:', error)
        })
      })