redux-observable获取当前位置

时间:2017-05-01 06:39:43

标签: react-native redux rxjs rxjs5 redux-observable

我尝试使用本地GeolocationgetCurrentPosition作出反应,然后在返回位置后立即使用react native geocoder来获取该位置。我使用redux-observable史诗来完成所有这些工作。

这是我的两部史诗:

location.epic.js

import { updateRegion } from '../map/map.action'
import Geocoder from 'react-native-geocoder'

export const getCurrentLocationEpic = action$ =>
  action$.ofType(GET_CURRENT_LOCATION)
    .mergeMap(() =>
      Observable.fromPromise(Geocoder.geocodePosition(makeSelectLocation()))
        .flatMap((response) => Observable.of(
          getCurrentLocationFulfilled(response)
        ))
        .catch(error => Observable.of(getCurrentLocationRejected(error)))
    )

export const getCurrentPositionEpic = action$ =>
  action$.ofType(GET_CURRENT_POSITION)
    .mergeMap(() =>
      navigator.geolocation.getCurrentPosition(
        (position) => Observable.of(
          updateRegion(position),
          getCurrentLocation(position)
        ),
        error => Observable.of(getCurrentPositionRejected(error)),
        { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
      ).do(x => console.log(x))
    ).do(x => console.log(x))

只要应用程序启动,此代码就会执行:

class Vepo extends Component {
  componentDidMount() {
    const { store } = this.context
    this.unsubscribe = store.subscribe(() => { })
    store.dispatch(fetchCategories())
    store.dispatch(getCurrentPosition())
  }

fetchCategories()也是一部具有史诗的动作,但这是有效的。调度getCurrentPosition()动作贯穿上面的史诗。我能看到的唯一输出是我的reducer处理getLocationRejected(),因为它控制台记录了这个:

there was an issue getting your current location:  Error: invalid position: {lat, lng} required
    at Object.geocodePosition (geocoder.js:15)
    at MergeMapSubscriber.project (location.epic.js:17)
    at MergeMapSubscriber._tryNext (mergeMap.js:120)
    at MergeMapSubscriber._next (mergeMap.js:110)
    at MergeMapSubscriber.Subscriber.next (Subscriber.js:89)
    at FilterSubscriber._next (filter.js:88)
    at FilterSubscriber.Subscriber.next (Subscriber.js:89)
    at Subject.next (Subject.js:55)
    at Object.dispatch (createEpicMiddleware.js:72)
    at Object.dispatch (devTools.js:313)

这是我的减速机:

const searchPage = (
  initialLocationState = initialState.get('searchForm').get('location'),
  action: Object): string => {
  switch (action.type) {
    case GET_CURRENT_LOCATION_FULFILLED: {
      return action.payload
    }
    case GET_CURRENT_LOCATION_REJECTED: {
      console.log('there was an issue getting your current location: ', 
        action.payload)
      return initialLocationState
    }
    case GET_CURRENT_POSITION_REJECTED: {
      console.log('there was an issue getting your current position: ', 
        action.payload)
      return initialLocationState
    }
    default:
      return initialLocationState
  }
}

有什么明显我做错了吗?我通过添加.do(x => console.log(x))进行调试的尝试什么也没做,没有任何内容记录到控制台。 updateRegion()永远不会触发,因为它会调度一个动作,而减速器UPDATE_REGION永远不会执行。但执行必须使其成为getCurrentPosition()的成功案例,例如:

(position) => Observable.of(
              updateRegion(position),
              getCurrentLocation(position)
            ),

必须执行,因为getCurrentLocation(position)确实已分派。

我哪里错了?

1 个答案:

答案 0 :(得分:3)

  

在采用回调函数的函数上使用史诗的技巧是什么? getCurrentPosition()接受回调,回调处理有效负载。基本上,如果你删除Observable.of(来自getCurrentPosition()内部,那就是getCurrentPosition()是如何被正确使用的 - 并且在没有redux-observable的情况下一直在为我工作。

在自定义Observable中包装任何内容都非常简单,非常类似于创建Promise,除了Observables是懒惰的 - 这一点很重要! RxJS Docs

在地理位置的情况下,有两个主要的API,getCurrentPositionwatchPosition。它们具有相同的语义,除了watchPosition每次位置更改时都会调用您的成功回调,而不仅仅是一次。让我们使用那个,因为它很自然地将其建模为流/可观察且最灵活。

function geolocationObservable(options) {
  return new Observable(observer => {
    // This function is called when someone subscribes.

    const id = navigator.geolocation.watchPosition(
      (position) => {
        observer.next(position);
      },
      error => {
        observer.error(error);
      },
      options
    );

    // Our teardown function. Will be called if they unsubscribe
    return () => {
      navigator.geolocation.clearWatch(id);
    };
  });
}

geolocationObservable({ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 })
  .subscribe(
    position => console.log(position),
    e => console.error(e)
  );
  // will log every time your location changes, until you unsubscribe

由于它现在是一个Observable,如果你只想要当前的位置,你可以.take(1)

所以在史诗中使用它可能就像这样

// If you want, you could also use .share() to share a single
// underlying `watchPosition` subscription aka multicast, but
// that's outside the scope of the question so I don't include it
const currentPosition$ = geolocationObservable({
  enableHighAccuracy: true,
  timeout: 20000,
  maximumAge: 1000
});

export const getCurrentPositionEpic = action$ =>
  action$.ofType(GET_CURRENT_POSITION)
    .mergeMap(() =>
      currentPosition$
        .take(1) // <----------------------------- only the current position
        .mergeMap(position => Observable.of(
          updateRegion(position),
          getCurrentLocation(position)
        ))
        .catch(error => Observable.of(
          getCurrentPositionRejected(error)
        ))
    );

作为旁注,您可能不需要同时发送updateRegion()getCurrentLocation()。你的减速器是否可以只听一个动作,因为它们似乎都表示同样的意图?