2017-06-04 118 views
0

每次地图标记更改时,我都想计算和更新我地图的区域。所以我想从地图上的标记创建一个可观察并订阅它:RxJS订阅永不执行

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

起初我只是console.logging标志物 - 但最终我将要运行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 
+0

当调用Observable.from时,observable是从'this.prop.markers'的**当前**值创建的。 'Observable.from'不会建立从数组到动态链接的可观察性。当'this.props.markers'发生变化时,你需要做'send'到'Subject'。 – 2017-06-04 01:53:54

+0

@torazaburo谢谢。我真的很努力去实现这个主题。当'props.markers'发生变化时我无法触发'next()' – BeniaminoBaggins

+0

JS中没有“观察”或“观察”某些变量或数组的概念。你必须安排知道什么时候'this.props.markers'改变,或者改变了,或者可能改变了。 – 2017-06-04 03:16:41

回答

1

[编辑]下面
是使用RX可观测量的解决方案,但它发布后快速,我在想:你确定你需要Observables的力量来实现你想要的吗?

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

我明白你想达到什么目的。首先,您必须了解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“警告我,当属性更改”。这正是你想要的。您想要捕捉对this.props.markers所做的任何更改并将其提供给您的可观察项。

我们将使用矩形的componentWillReceivePropshttps://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) 
} 

现在,这将触发您的markers$上的subscribe呼叫。

+0

谢谢。你可能是对的,我不需要可观察的东西。但从理论上讲,教我一点RxJS,在你的答案中用可观察的方法解决问题,我会在哪里调用'getRegionForCoordinates'然后调用'this.props.updateRegion'?我会在'componentDidMount'中添加一个订阅到'this.markers $'吗?与此类似,但调用我的两个函数? '.subscribe('='> +>), error:err => console.error console.log('done'), });' – BeniaminoBaggins

+1

准确地说,您会在'componentDidMount'中添加一个'subscribe',并从那里调用'getRegionForCoordinates'和'this.props.updateRegion'。警告,如果你没有使用箭头函数进行订阅(或下一个)回调,你将会将回调绑定到'this',否则它不会在回调中定义('markers $ .subscribe(function callback() {/*..*/} .bind(this))') – atomrc