React-mapbox-gl: Geocoder capability

Created on 15 Feb 2017  路  26Comments  路  Source: alex3165/react-mapbox-gl

Hiya! Is there an easy way to implement a search bar in my map through this package?
If not, are there plans to implement this?

Link to mapbox-gl example: https://www.mapbox.com/mapbox-gl-js/example/mapbox-gl-geocoder/

Question

Most helpful comment

In case anyone else ends up here, an easy fix is to create a geocoder component...

import { createElement, Component } from 'react';
import { Map } from 'mapbox-gl';
import PropTypes from 'prop-types';
import { accessToken } from './token';
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css';
import * as MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';

class Geocoder extends Component {
  static contextTypes = { map: PropTypes.object.isRequired };

  context: {
    map: Map;
  };

  componentDidMount() {
    const { map } = this.context;

    map.addControl(
      new MapboxGeocoder({
        accessToken
      })
    );
  }

  render() {
    return null;
  }
}

export default Geocoder;

And then include it in your map...

const MapBoxMap = ReactMapboxGl({ accessToken });

const Map = () =>
    <MapBoxMap
      style="..."
      containerStyle={{
        height:  '100vh',
        width: '100vw'
      }}
    >
      <Layer type="symbol" id="marker" layout={{ 'icon-image': 'marker-15' }}>
        <Feature coordinates={[-0.481747846041145, 51.3233379650232]} />
      </Layer>
      <Geocoder />
    </MapBoxMap>

Edit: added context types as discussed below

All 26 comments

Hey @evanfrawley There is no plan to support this component, I believe this kind of react control should live in a different repository, If you work on this let me know I will make sure to reference it in the documentation.

Thank @alex3165 I'll see if I can implement it and will report back if I can get it working. Thanks for the quick response!

Update: I forked this repo and fiddled with the map.tsx file. I was able to implement the geocoder in a few changed in the code, but not without putting Mapbox's CSS file into the index.html. When I tried to import it within the Typescript, the npm run build command didn't like reading in the mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css file.

_Did you import the Mapbox CSS via the import statement from the library?_ The geocoder library wouldn't properly import with the line import * as MapboxGlGeocoder from 'mapbox-gl-geocoder/dist/mapbox-gl-geocoder.min'. The min.js doesn't seem to load in it's required CSS either.

Hey @evanfrawley, The css of mapbox gl is manually converted to a string https://github.com/alex3165/react-mapbox-gl/blob/master/src/constants/css.ts . Although I would suggest you to do the geocoder yourself without using the control provided by mapbox-gl-js, it is just a simple input after all then you would need to listen a change on it and request their geocoding API https://www.mapbox.com/api-documentation/#geocoding accordingly to get the data

In case anyone else ends up here, an easy fix is to create a geocoder component...

import { createElement, Component } from 'react';
import { Map } from 'mapbox-gl';
import PropTypes from 'prop-types';
import { accessToken } from './token';
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css';
import * as MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';

class Geocoder extends Component {
  static contextTypes = { map: PropTypes.object.isRequired };

  context: {
    map: Map;
  };

  componentDidMount() {
    const { map } = this.context;

    map.addControl(
      new MapboxGeocoder({
        accessToken
      })
    );
  }

  render() {
    return null;
  }
}

export default Geocoder;

And then include it in your map...

const MapBoxMap = ReactMapboxGl({ accessToken });

const Map = () =>
    <MapBoxMap
      style="..."
      containerStyle={{
        height:  '100vh',
        width: '100vw'
      }}
    >
      <Layer type="symbol" id="marker" layout={{ 'icon-image': 'marker-15' }}>
        <Feature coordinates={[-0.481747846041145, 51.3233379650232]} />
      </Layer>
      <Geocoder />
    </MapBoxMap>

Edit: added context types as discussed below

Good job @bsouthga ! 馃憤

Hi @bsouthga I just came accross this example and was wondering if you could add how you passed the context through to the <Geocoder/>component. I'm just starting with the library and am having a little difficulty. Thanks!

Hey @taystu, the context should be available if you put the geocoder inside a Map component from this library, for example:

const MapBoxMap = ReactMapboxGl({ accessToken });

const Map = () =>
    <MapBoxMap
      containerStyle={{
        height:  '100vh',
        width: '100vw'
      }}
    >
      <Layer type="symbol" id="marker" layout={{ 'icon-image': 'marker-15' }}>
        <Feature coordinates={[-0.481747846041145, 51.3233379650232]} />
      </Layer>
      <Geocoder />
    </MapBoxMap>

Thanks for getting back to me @bsouthga I ended up fixing it by changing context: { map: Map; };
to
static contextTypes = { map: PropTypes.object.isRequired };

I suppose the former is typescript syntax? I'm not using typescript, but regardless thanks for the response.

I suppose

ah yeah good point! I forgot you need to add the context prop types.

Has anyone run into an issue where using
import * as MapboxGeocoder from '@mapbox/mapbox-gl-geocoder' throws an error saying could not find the module @mapbox/mapbox-gl-geocoder?

I don't know how mapbox export their module but have you tried

import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder'

The module seems to be available:
https://unpkg.com/@mapbox/mapbox-gl-geocoder

However if you are using Typescript is seems that there is no type definitions for @mapbox/mapbox-gl-geocoder.

It's apparently not there by default. I needed to npm install mapbox-gl-geocoder and then it said it installed under the @mapbox namespace but it's actually not. I imported it from mapbox-gl-geocoder and it works.

the line of map.addControl is giving me an error if cannot read property of undefinded.

Console logging the context returns a blank object.

I also tried calling addControl directly onto Map but it says it's not a function.

Any help?

@tmerrr you might need to specify contextTypes as @taystu notes above.

Thank you, that was exactly it. Hadn't completely understood the above answer initially! Below solution to hopefully help others:

import { Component } from 'react';
import PropTypes from 'prop-types';
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css';
import * as MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';

class Geocoder extends Component {

  componentDidMount() {
    const { map } = this.context;

    map.addControl(
      new MapboxGeocoder({
        accessToken: 'your token'
      })
    );
  }

  render() {
    return null;
  }

  static contextTypes = {
    map: PropTypes.object.isRequired
  };
}

export default Geocoder;

Hi, nice solution. But, could someone explain this syntax:

static contextTypes = { map: PropTypes.object.isRequired };

Is this a old react way of using context ?

@jerlam06 this stumped me when I came across it on another issue. It is the legacy context api, see https://reactjs.org/docs/legacy-context.html

Hey guys, thanks for all work. Really you help me a lot.

Jus a comment for people that are receiving this error:

could not find the module @mapbox/mapbox-gl-geocoder

for my side, the problem was solved after install "es6-promise" with:

yarn add es6-promise

Thank you so much @bsouthga for the response

However, MapboxGeocoder is the default export
Therefore I did
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';
instead of
import * as MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';

Only reason I mention this is for testing. This will allow you to mock the package using jest
jest.mock('@mapbox/mapbox-gl-geocoder');

Is it still working for you ? this.context is always undefined for me

i got it working as per @bsouthga suggestion above ^^ - But had to tweak to get it to stop erroring. Changed context to an object... not sure why that works. Not familiar with a lot of the Context/newer features of react.

So it went from

 context: {
    map: Map;
  };

to

 context = {
    map: Map;
  };

Bizarrely that worked.

One thing though - Can't seem to style the Geocoder component. Anyone know?

Is there a method to do this for React Native?
Currently, looking to add geocode functionality into my react native app.

Hi I'm trying to get this working and also getting the error: addControl undefined. I've implemented @tmerrr solution and the first one indicated here and can't get it to work. I know this thread is old but does anyone have any updates on this?

App.tsx:

```import React, { useRef, useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import ReactMapboxGl, { Layer, Feature } from 'react-mapbox-gl';
import { MapControl } from './MapControl';
import * as MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';

const Map = ReactMapboxGl({
accessToken: 'xxx',
})

export const MapContext = React.createContext({})

function App() {

const mapRef = useRef(null)

return (



style="mapbox://styles/mapbox/streets-v9"
containerStyle={{
height: '80vh',
width: '80vw'
}}
ref={mapRef}
>


new MapboxGeocoder({
accessToken: 'xxx'
})} />




);
}

export default App;


MapControl.tsx:

import { useContext } from 'react';
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css';
import { MapContext } from './App';

interface MapControlProps {
control: any
}

function MapControl(props: MapControlProps) {

const { mapRef } = useContext(MapContext)

const {map} = mapRef.current.state

map.addControl(props.control)

return null

}

export {MapControl}
```

In my case to solve for the addControl is undefined error this was enough:
const mapRef = useRef<any>(null)
or
const mapRef: any = useRef();

Indstead of only const mapRef = useRef();

Was this page helpful?
0 / 5 - 0 ratings

Related issues

PawkaHub picture PawkaHub  路  17Comments

suyesh picture suyesh  路  11Comments

Beeze picture Beeze  路  15Comments

rlueder picture rlueder  路  13Comments

radeno picture radeno  路  13Comments