Kurt Peek
Kurt Peek

Reputation: 57701

How to setState() from within a nested function in React?

I'm trying to adapt this example from https://github.com/mapbox/mapbox-react-examples/tree/master/basic,

import React from 'react'
import ReactDOM from 'react-dom'
import mapboxgl from 'mapbox-gl'

mapboxgl.accessToken = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4M29iazA2Z2gycXA4N2pmbDZmangifQ.-g_vE53SD2WrJ6tFX7QHmA';

class Application extends React.Component {

  constructor(props: Props) {
    super(props);
    this.state = {
      lng: 5,
      lat: 34,
      zoom: 1.5
    };
  }

  componentDidMount() {
    const { lng, lat, zoom } = this.state;

    const map = new mapboxgl.Map({
      container: this.mapContainer,
      style: 'mapbox://styles/mapbox/streets-v9',
      center: [lng, lat],
      zoom
    });

    map.on('move', () => {
      const { lng, lat } = map.getCenter();

      this.setState({
        lng: lng.toFixed(4),
        lat: lat.toFixed(4),
        zoom: map.getZoom().toFixed(2)
      });
    });
  }

  render() {
    const { lng, lat, zoom } = this.state;

    return (
      <div>
        <div className="inline-block absolute top left mt12 ml12 bg-darken75 color-white z1 py6 px12 round-full txt-s txt-bold">
          <div>{`Longitude: ${lng} Latitude: ${lat} Zoom: ${zoom}`}</div>
        </div>
        <div ref={el => this.mapContainer = el} className="absolute top right left bottom" />
      </div>
    );
  }
}

ReactDOM.render(<Application />, document.getElementById('app'));

to a case in which, rather than displaying the map's center, I would like to display the latitude and longitude of the mouse position.

So far, I've managed to simply log it to the console:

import React from 'react';
import mapboxgl from 'mapbox-gl';

mapboxgl.accessToken = 'pk.eyJ1Ijoia3VydHBlZWsiLCJhIjoiY2p6cnVneWdvMHlzeDNqcWo0dm83ZzZ2eiJ9.yUCSreTRcKs12uT5PTCztg';


export default class Map extends React.Component {
  componentDidMount() {
    this.map = new mapboxgl.Map({
      container: this.mapContainer,
      style: 'mapbox://styles/mapbox/outdoors-v11',
      center: [-119.5591, 37.715],
      zoom: 9
    });

    this.map.on('load', function(e) {    
      e.target.on('mousemove', function(e) {
        console.log(JSON.stringify(e.point));
        console.log(JSON.stringify(e.lngLat.wrap()));
      });
    });
  }

  componentWillUnmount() {
    this.map.remove();
  }

  render() {
    const style = {
      position: 'absolute',
      top: 0,
      bottom: 0,
      width: '100%'
    };

    return <div style={style} ref={el => this.mapContainer = el} />;
  }
}

This writes lines like the following to the console:

{"x":972,"y":272}
{"lng":-118.90266689452113,"lat":37.86205552587528}

However, rather than logging the coordinates to the console, I would like to invoke this.setState() like in the example so that I can render the coordinates in a child component.

The problem is, within the on('mousemove', ...) callback function, this is not the component. I've read about using arrow functions (which are lexically scoped) to work around this, but it seems to me that in this case, I need a 'normal' function(e) in order to capture the event.

How can I setState() with the mouse coordinates in this example?

Upvotes: 3

Views: 486

Answers (1)

Dupocas
Dupocas

Reputation: 21347

It's possible to use arrow functions just like any other function

this.map.on('load', e => {    
  e.target.on('mousemove', e => {
      this.setState({}) //correct this
  })
})

Upvotes: 2

Related Questions