Hi,
Working on a project that is getting quite large and I was wondering if there is a simple solution to using the map outside of the component. At the moment I am using a ref in order to use getMap() within the map component, but I have a search function that needs to flyTo the location when the user clicks on the result and I would like to be able to do so within another component. Is that possible or is everything going to need to funnel back into the Map component? I've looked everywhere for an example and have found nothing so I believe that I won't to in the manner that I want to do it. If I can't that's fine, but it would suck if there is and I just absolutely pass it up only to make it more complicated.
I use context inside React to get access to the map instance:
// MapContext.js
import React, {createContext, useState} from 'react';
// create context object
const MapContext = createContext();
// handle state in your provider and pass it as the value
export function MapProvider(props){
const [map, setMap] = useState();
return <MapContext.Provider value={[map, setMap]} {...props} />
}
// expose a helper hook to easily grab the state anywhere in your app
// This is an excellent pattern to share state without redux - and be
// wary of how you can optimise it:
// https://kentcdodds.com/blog/how-to-optimize-your-context-value
export function useMap(){
const context = useContext(MapContext);
if(context === undefined) throw Error('You forgot to wrap your app with <MapProvider />');
return context;
}
// App.js
import React from 'react';
import {MapProvider} from './MapContext';
export default function App(){
const [, setMap] = useMap();
return (
<MapProvider>
<Example />
<ReactMapGL
...
ref={ref => ref && setMap(ref.getMap())}
/>
</MapProvider>
)
}
// Example.js
import React from 'react';
import {useMap} from './MapContext';
export default function Example(){
// get the map instance outside the component and do whatever!
const [map] = useMap();
const onClick = () => {
map && map.flyTo(...);.
}
useEffect(() => alert('map ready!'), [map])
return <button onClick={onClick}>Fly like a bird</button>
}
Note; I have switched between using useRef and useState to store the context. They both seem to have tradeoffs; useRef is probably the intended usage according to dan, but this makes it difficult to listen to it actually being set, i.e. with useEffect. Depending on ref.current is problematic though.
useState on the other hand will trigger an effect re-run. Mutations to the map object should not trigger a re-render because it's the same object, just mutated.
Maybe a feature request to expose a useMap hook? 馃檹 @Pessimistress accepting pull requests for hooks in this library? Don't think there are any just yet.
Hello ! I would like to re-open this one, i'm having issue with own code base to get ref on the map to use the method flyTo. The example ahead just does not work, my code simplified
import ReactMapboxGl, { Layer, Feature, Marker } from "react-mapbox-gl";
const Map = ReactMapboxGl({
accessToken: process.env.REACT_APP_MAPBOX_API
});
<Map
// eslint-disable-next-line
ref={ref => console.log(ref)}
style="mapbox://styles/mapbox/streets-v9"
containerStyle={{
height: "93vh",
width: "100%"
}}
/>
I tried to find the method flyTo in this ref, but could not find it.
This is not going to work because you鈥檙e trying to instantiate a component instead of render it. Use it as a component and pass mapboxApiAccessToken as a prop instead.
Sorry for your fast answer @mayteio, just a mistake from me, i've just mistaken your library with react-mapbox-gl !!
Maybe a feature request to expose a useMap hook?
Yes! All PRs welcomed.
I'd be happy to @Pessimistress. What are your thoughts on API design? My solution above requires a context (I think all solutions would?) to provider the ref down to other components. That would mean introducing a <MapRefProvider /> or something, increasing API surface. Are you opposed to this approach?
I'll open and work on a PR and we can discuss there.
MapContext will be available in v5.3/v6.1. You can try it with 6.1.0-beta.1.
Most helpful comment
I use context inside React to get access to the map instance:
Note; I have switched between using
useRefanduseStateto store the context. They both seem to have tradeoffs; useRef is probably the intended usage according to dan, but this makes it difficult to listen to it actually being set, i.e. withuseEffect. Depending on ref.current is problematic though.useStateon the other hand will trigger an effect re-run. Mutations to the map object should not trigger a re-render because it's the same object, just mutated.Maybe a feature request to expose a
useMaphook? 馃檹 @Pessimistress accepting pull requests for hooks in this library? Don't think there are any just yet.