RxJS订阅永远不会执行

时间:2017-06-04 01:27:57

标签: reactjs react-native rxjs rxjs5

每当地图标记发生变化时,我想计算并更新地图的区域。所以我试图从地图的标记创建一个observable并订阅它:

class Map extends Component {

  componentDidMount() {
    const { store } = this.context
    this.unsubscribe = store.subscribe(() => { })
    const markers$ = Observable.from(this.props.markers)
    markers$.subscribe(x => console.log('observable', x))
  }

最初我只是控制台。记录标记 - 但最终我会想要在标记发生变化时运行getRegionForCoordinates然后this.props.updateRegion。什么都没有登录到控制台。我究竟做错了什么? this.props.markers最初是一个空数组。

Map.js

import { StyleSheet } from 'react-native'
import React, { Component } from 'react'
import MapView from 'react-native-maps'
import { connect } from 'react-redux'
import {
  Button,
  Container
} from 'native-base'
import { Observable } from 'rxjs'

import selectMarkers from './markers.selector'

import { updateRegion } from './map.action'
import Icon from 'react-native-vector-icons/FontAwesome'
import { toggleMenu } from '../search-page/searchPage.action'
import mapStyle from './style'

const mapStateToProps = (state) => ({
  region: state.get('map').get('region'),
  markers: selectMarkers(state)
})

const mapDispatchToProps = (dispatch) => ({
  onRegionChange: (region) => {
    dispatch(updateRegion(region))
  },
  onToggleMenuClick: () => {
    dispatch(toggleMenu())
  }
})



const getRegionForCoordinates = (points) => {
  // points should be an array of { latitude: X, longitude: Y }
  let minX, maxX, minY, maxY;

  // init first point
  ((point) => {
    minX = point.latitude
    maxX = point.latitude
    minY = point.longitude
    maxY = point.longitude
  })(points[0])

  // calculate rect
  points.map((point) => {
    minX = Math.min(minX, point.latitude)
    maxX = Math.max(maxX, point.latitude)
    minY = Math.min(minY, point.longitude)
    maxY = Math.max(maxY, point.longitude)
  })

  const midX = (minX + maxX) / 2
  const midY = (minY + maxY) / 2
  const deltaX = (maxX - minX)
  const deltaY = (maxY - minY)

  return {
    latitude: midX,
    longitude: midY,
    latitudeDelta: deltaX,
    longitudeDelta: deltaY
  }
}

class Map extends Component {

  componentDidMount() {
    const { store } = this.context
    this.unsubscribe = store.subscribe(() => { })
    const markers$ = Observable.from(this.props.markers)
    markers$.subscribe(x => console.log('observable', x))
  }

  componentWillUnmount() {
    this.unsubscribe()
  }

  render() {
    return (
      <Container>
        <MapView
          style={styles.map}
          region={this.props.region}
          onRegionChangeComplete={this.props.onRegionChange}
        >
          {
            this.props.markers.map(marker => {
              return (
                <MapView.Marker
                  coordinate={{ latitude: marker.latitude, longitude: marker.longitude }}
                  title={marker.name}
                />
              )
            })}
        </MapView>
        <Button
          small
          icon
          style={mapStyle.toggleMenuButton}
          onPress={() => this.props.onToggleMenuClick()}>
          <Icon name="sliders" size={20} color="#FFFFFF" />
        </Button>
      </Container>
    )
  }
}

Map.contextTypes = {
  store: React.PropTypes.object
}

Map.propTypes = {
  region: React.PropTypes.shape({
    latitude: React.PropTypes.number,
    longitude: React.PropTypes.number,
    latitudeDelta: React.PropTypes.number,
    longitudeDelta: React.PropTypes.number
  }).isRequired,
  onRegionChange: React.PropTypes.func.isRequired,
  onToggleMenuClick: React.PropTypes.func.isRequired,
  markers: React.PropTypes.array
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Map)

const styles = StyleSheet.create({
  map: {
    ...StyleSheet.absoluteFillObject,
    zIndex: -1
  }
})

markers.selector.js

import { createSelector } from 'reselect'


const searchResultsSelector = state => {
  return state.get('searchResults')
}

const selectMarkers = createSelector(
  searchResultsSelector,
  (searchResults) => {
    const shops = searchResults ? searchResults.map(result => {
      return result.shops.map(shop => {
        return {
          id: shop.f1,
          name: shop.f2,
          latitude: shop.f4,
          longitude: shop.f3
        }
      })
    }) : searchResults
    const shopIds = []
    const flattenedShops = [].concat.apply([], shops)
    const uniqueShops = flattenedShops.map(shop => {
      if (!shopIds.includes(shop.id)) {
        shopIds.push(shop.id)
        return shop
      }
    })
    const finalMarkers = uniqueShops.filter(n => n != undefined)
    return finalMarkers
  }
)

export default selectMarkers

1 个答案:

答案 0 :(得分:1)

[编辑]
下面是使用rx observables的解决方案,但在发布之后很快,我在想:您确定需要Observables的强大功能来实现您的目标吗?

我认为您应该考虑仅使用componentWillReceiveProps这是React方式来观察变化。我认为它可能非常适合你的需求 您可以在此回调中完全触发getRegionForCoordinatesthis.props.updateRegion,而无需任何可观察的内容。
[结束编辑]

我看到你想要实现的目标。首先,您必须了解Observable.from只接受单个对象/值,然后忽略对初始对象所做的任何更改。

const value = [1]
const obs$ = Observable.from(arr)
obs$.subscribe(console.log.bind(console)) // < this will only print once
value.push(2); // < this will be ignored and wont fire anything

但是,希望对你而言,有一种方法可以告诉我React&#34;当物业发生变化时警告我#34;这正是你想要的。您希望捕获对this.props.markers所做的任何更改,并将其提供给您的可观察对象。

我们将使用Rect componentWillReceiveProps https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops来做到这一点

我们需要做的第一件事就是保留对marker$的引用(因此可以通过其他方法访问它:

componentDidMount() {
  //...
  this.markers$ = new Observable.Subject()
  //you can subscribe here to this observable
  this.markers$.next(this.props.markers)
}

我们还需要实施将componentWillReceiveProps

提供的markers$
componentWillReceiveProps(nextProps) {
  this.markers$.next(nextProps.markers)
}

现在,这会触发subscribe上的markers$来电。