React-Native consume user’s location with redux-saga

Consume the user's location using the navigation API, and open a channel to emit the position changes.

Intro

The following snippet uses React-Native geolocation.

From their documentation: "As a browser polyfill, this API is available through the navigator.geolocation global - you do not need to import it."

You may want to read about redux-saga's channels.

Define the actions

This script contains the action that will be logged when the user's location changes.

// ../Actions/Location.js

import { createAction } from 'redux-actions'

export const LOCATION_CHANGE = 'LOCATION_CHANGE'

export const constants = {  
  LOCATION_CHANGE
}

export const change = createAction(LOCATION_CHANGE)  

Create a Saga's channel

This script will be consuming the user location and emitting events in a saga's event-channel.

// ../Services/Location.js

import { eventChannel, END } from 'redux-saga'

const { watchPosition } = navigator.geolocation

// Configuration for the `geolocation.watchPosition`.
const WATCH_POSITION_OPTIONS = {  
  enableHighAccuracy: true,
  timeout: 20000,
  maximumAge: 1000,
  distanceFilter: 10
}

// Define the function to open the location listener.
export function locationChangeChannel () {  
  // Return the event channel.
  return eventChannel((emit) => {
    // Close the channel after any errors in `watchPosition`.
    const onError = (error) => emit(END)

    // Invokes the `emit` callback whenever the location changes.
    const watchId = watchPosition(emit, onError, WATCH_POSITION_OPTIONS)

    // The `eventChannel` call should return the unsubscribe
    // function, this will stop watching of the location of the user.
    return () => navigator.geolocation.clearWatch(watchId)
  })
}

export default locationChangeChannel  

Consume the channel in a Saga.

This is the script that will consume the events from the eventChannel and log the LOCATION_CHANGE action.

// ../Sagas/Location.js

import { all, call, put, takeEvery } from 'redux-saga/effects'

import { change } from '../Actions/Location'  
import { locationChangeChannel } from '../Services/Location'

function * locationChange ({ coords }) {  
  // The `latitude`, `longitude`
  // parameters come from `watchPosition`.
  const { latitude, longitude } = coords

  // Log the `LOCATION_CHANGE` action.
  yield put(change({ latitude, longitude }))
}

function * openLocationWatch () {  
  const channel = yield call(locationChangeChannel)
  yield takeEvery(channel, locationChange)
}

export default function * LocationSagas () {  
  yield all([
    openLocationWatch()
  ])
}