2017-06-04 153 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