redux-observable 获取当前位置

Ben*_*ins 3 rxjs react-native redux rxjs5 redux-observable

我试图使用原生的反应地理位置getCurrentPosition,然后尽快返回的位置,使用反应天然地理编码器使用该位置得到的位置。我正在使用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))
Run Code Online (Sandbox Code Playgroud)

一旦应用程序启动,此代码就会执行:

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

fetchCategories()是一个也有史诗的动作,但这是有效的。调度getCurrentPosition()动作贯穿了上面的史诗。我能看到的唯一输出是我的减速器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)
Run Code Online (Sandbox Code Playgroud)

这是我的减速器:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

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

(position) => Observable.of(
              updateRegion(position),
              getCurrentLocation(position)
            ),
Run Code Online (Sandbox Code Playgroud)

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

我哪里错了?

jay*_*lps 5

在带有回调函数的函数上使用史诗的技术是什么?getCurrentPosition() 接受一个回调,回调处理负载。基本上,如果您从 getCurrentPosition() 中删除 Observable.of(,这就是正确使用 getCurrentPosition() 的方式 - 并且在没有 redux-observable 的情况下一直在为我工作。

在自定义 Observable 中包装任何东西都相当简单,与创建 Promise 非常相似,只是 Observables 是惰性的——理解这一点很重要!RxJS 文档

在地理定位的情况下,有两个主要的 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
Run Code Online (Sandbox Code Playgroud)

由于它现在是一个 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)
        ))
    );
Run Code Online (Sandbox Code Playgroud)

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