GeoSearch
<GeoSearch google={object} children={function} // Optional parameters initialZoom={number} initialPosition={object} enableRefine={boolean} enableRefineOnMapMove={boolean} />
About this widget
The GeoSearch
widget displays search results on a Google Maps map.
It lets you search for results based on their position, but also provides some of the common geo search patterns such as search on map interactions.
All the other geo components need to be nested under it. All the options available on the Google Maps class can be provided as props.
Requirements
The API of this widget is a bit different than the others that you can find in React InstantSearch. It is component-driven rather than options-driven. We chose the former because it brings more flexibility to the widget. Since the geo search pattern is not a use case for every application, we decided to ship the widget in a separate package. Be sure to install it before using it:
$
npm install --save react-instantsearch-dom-maps
The GeoSearch
widget uses the geo search capabilities of Algolia. Your hits must have a _geoloc
attribute to be available in the render prop.
The feature is currently incompatible with multiple values in the _geoloc
attribute (e.g., a restaurant with multiple locations). In that case, you can duplicate your records and use the distinct feature of Algolia to retrieve only the most relevant result.
You are responsible for loading the Google Maps library. We provide the <GoogleMapsLoader />
component to load the library but its usage is not required to use the geo widget. You can use any strategy you want to load Google Maps. You can find more informations in the Google Maps documentation.
Don’t forget to explicitly set the height
of the map container (see below), otherwise it won’t show.
Examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import {
GoogleMapsLoader,
GeoSearch,
Control,
Marker,
} from 'react-instantsearch-dom-maps';
<div style={{ height: 500 }}>
<GoogleMapsLoader apiKey="GOOGLE_MAPS_API_KEY">
{google => (
<GeoSearch google={google}>
{({ hits }) => (
<div>
<Control />
{hits.map(hit => (
<Marker key={hit.objectID} hit={hit} />
))}
</div>
)}
</GeoSearch>
)}
</GoogleMapsLoader>
</div>
Props
google
|
type: object
Required
A reference to the global |
||
Copy
|
|||
children
|
type: function
Required
The render function takes an object as argument with the |
||
Copy
|
|||
initialZoom
|
type: number
default: 1
Optional
By default, the map sets the zoom based on to the markers that are displayed on it. Yet when React InstantSearch refines the results, they may be empty. When it happens, it needs a zoom level to render the map. |
||
Copy
|
|||
initialPosition
|
type: object
default: { lat: 0, lng: 0 }
Optional
By default, the map sets the position based on to the markers that are displayed on it. Yet when React InstantSearch refines the results, they may be empty. When it happens, it needs a position to render the map. |
||
Copy
|
|||
enableRefine
|
type: boolean
default: true
Optional
If |
||
Copy
|
|||
enableRefineOnMapMove
|
type: boolean
default: true
Optional
If |
||
Copy
|
Customize the UI - connectGeoSearch
If you want to create your own UI of the GeoSearch
widget or use another UI library, you can use connectors.
Connectors are higher-order components. They encapsulate the logic for a specific kind of widget and they provide a way to interact with the InstantSearch context.
They have an outer component API that we call exposed props, and they provide some other props to the wrapped components which are called the provided props.
It’s a 3-step process:
// 1. Create a React component
class GeoSearch extends Component {
render() {
// return the DOM output
}
}
// 2. Connect the component using the connector
const CustomGeoSearch = connectGeoSearch(GeoSearch);
// 3. Use your connected widget
<CustomGeoSearch />
Create a React component
class GeoSearch extends Component {
render() {
const {
object[] hits,
object position,
object currentRefinement,
boolean isRefinedWithMap,
function refine,
function createURL,
} = this.props;
// return the DOM output
}
}
Provided Props
The examples built with the connector use Leaflet to render the map. Make sure to have the library correctly setup before trying the demo. You can find more details in the Leaflet documentation. We picked Leaflet but you can use any library you prefer (e.g., Google Maps, Mapbox, etc.)
hits
|
type: object[]
The hits that matched the search request. |
||
Copy
|
|||
position
|
type: object
The current position of the search, when applicable. |
||
Copy
|
|||
currentRefinement
|
type: object
The current bounding box of the search, with:
|
||
isRefinedWithMap
|
type: boolean
Returns |
||
refine
|
type: function
Sets a bounding box to filter the results from the given map bounds. The function accepts an object with:
|
||
createURL
|
type: function
Generates a URL for the corresponding search state. |
Create and instantiate your connected widget
<CustomGeoSearch
// Optional parameters
defaultRefinement={object}
/>
Exposed Props
defaultRefinement
|
type: object
Required
Default search state of the widget containing the bounds for the map. The object contains:
|
||
Copy
|
Full example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import { connectGeoSearch } from 'react-instantsearch-dom';
class GeoSearch extends Component {
isUserInteraction = true;
markers = [];
componentDidMount() {
const { refine } = this.props;
this.instance = L.map(this.el);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution:
'© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
}).addTo(this.instance);
this.instance.on('moveend', () => {
if (this.isUserInteraction) {
const ne = this.instance.getBounds().getNorthEast();
const sw = this.instance.getBounds().getSouthWest();
refine({
northEast: { lat: ne.lat, lng: ne.lng },
southWest: { lat: sw.lat, lng: sw.lng },
});
}
});
}
componentDidUpdate() {
const { hits, currentRefinement, position } = this.props;
this.markers.forEach(marker => marker.remove());
this.markers = hits.map(({ _geoloc }) =>
L.marker([_geoloc.lat, _geoloc.lng]).addTo(this.instance)
);
this.isUserInteraction = false;
if (!currentRefinement && this.markers.length) {
this.instance.fitBounds(L.featureGroup(this.markers).getBounds(), {
animate: false,
});
} else if (!currentRefinement) {
this.instance.setView(
position || {
lat: 48.864716,
lng: 2.349014,
},
12,
{
animate: false,
}
);
}
this.isUserInteraction = true;
}
render() {
const { currentRefinement, refine } = this.props;
return (
<div>
<div style={{ height: 500 }} ref={c => (this.el = c)} />
{Boolean(currentRefinement) && (
<button onClick={() => refine()}>Clear map refinement</button>
)}
</div>
);
}
}
const CustomGeoSearch = connectGeoSearch(GeoSearch);