mardi 2 février 2016

Location tracking react component - How to to handle listeners/updates

I have a "dumb" component that displays the approx. distance between two locations.

import React from "react"

const cos = Math.cos
const p = Math.PI / 180
function getDistance(lat1, lon1, lat2, lon2) {
  var a = 0.5 - c((lat2 - lat1) * p)/2 +
          c(lat1 * p) * c(lat2 * p) *
          (1 - c((lon2 - lon1) * p))/2
  return 12742 * Math.asin(Math.sqrt(a)) // 2 * R; R = 6371 km
}

let Distance = ({from,to}) => {
    let distance = getDistance(
      from.latitude,
      from.longitude,
      to.latitude,
      to.longitude)
    if (distance < 1.0) {
      distance = (distance * 100) + "m"
    } else {
      distance = distance + "km"
    }
    return <span>{distance}</span>
  }
}
Distance.displayName = "Distance"

I want to enhance this component by updating the component whenever the browsers geo-location changes. So I created another component

class TrackingDistance extends React.Component {
  constructor(props) {
    super(props)
    this.updatePosition = this.updatePosition.bind(this)
    if (TrackingDistance.lastPosition) {
      this.updatePosition(TrackingDistance.lastPosition)
    } else {
      navigator.geolocation.getCurrentPosition(this.updatePosition)
    }
  }

  updatePosition(position) {
    this.setState({from: position.coords})
  }

  componentDidMount() {
    if (TrackingDistance.instances.length === 0) {
      TrackingDistance.watchId = navigator.geolocation.watchPosition(position => {
        TrackingDistance.instances.map(instance => instance.updatePosition(position))
      })
    }
    TrackingDistance.instances.push(this)
  }

  componentWillUnmount() {
    TrackingDistance.instances.remove(this)
    if (TrackingDistance.instances.length === 0) {
      navigator.geolocation.clearWatch(TrackingDistance.watchId)
    }
  }

  render() {
    return this.state.from ? <Distance to={this.props.to} from={this.state.from}/> : null
  }
}
TrackingDistance.instances = []
TrackingDistance.watchId = 0
TrackingDistance.lastPosition = null

My goal was to register the event listener for the geo-location updates only once in my app and not one listener for every component. I worry I create a circular reference when I save the component instances in a static array TrackingDistance.instances.

Also is it allowed to call the setState method of other components from outside the component itself (like I do in my location listener?)

Is there any other react pattern to solve such problem better? I can't use Mixins, cause I am using ES6 React component classes.

Aucun commentaire:

Enregistrer un commentaire