2

I created a vue component based on MapBox, which is restricted in initializations before it costs money and that is perfectly fine. But I want to reduce reinitializations of my map component for their and my sake. That's why I thought if it is possible to define the component once, pass in some properties and then handle the state via vuex.

Right now, I'd have to import my component and add the data like this:

<Map
  :sources="geoData.sources"
  :layers="geoData.layers"
  :mapOptions="mapOptions"
  :componentOptions="{ drawingEnabled: toggleMapDrawing, activeLayers: activeMapLayers, activeMarkerGroups: [] }"
  @loaded="onMapLoaded" @selectedMarkers="onSelectedObjects"/>

The componentOptions are being watched, so the component changes its state accordingly. My ideas/approaches so far were the following:

  1. I thought about adding the snippet above to the root vue file, but that won't help since I want to place the map component dynamically and not statically before the rest of the page content.
  2. Passing a rendered vue component into a variable and appending that later would be a bit too hacky, if it is even possible.
  3. Using slots, but from what I've seen in the docs, it's not possible to use a slotted component from a parent component in a child like this.
  4. The best idea that has come to my mind was to define the actual MapBox variable (which I suppose triggers the API for initialization) and then save that globally using the store or something. But since that will immediately append the component to a DOM element that will be specified in the options, so I'd have to store that somehow, too.

The initialization of the map happens in the mounted hook of the component and looks like this:

const baseOptions = {
  accessToken: process.env.MAPBOX_TOKEN,
  container: 'map',
  style: process.env.MAPBOX_STYLE_URL,
  minZoom: 10,
  maxZoom: 20,
  zoom: 13,
  bearing: 150,
  pitch: 50
}
this.map = new mapboxgl.Map(Object.assign(baseOptions, this.mapOptions))
if (!this.map) { throw new Error('Could not create map. Make sure the token is valid.') }

I might be wrong, maybe there's a better way or maybe this whole idea might be garbage, but hopefully it's not. Also please note that I'm not using the vue-mapbox module, because it's not being maintained anymore.

I'm thankful for any ideas and hints :)

2
  • If I am understanding this correctly after a quick read; you wish for the Map component to load in once and stay as static for the rest of your application lifecycle? You won't be changing the passed props to it after its first initialization? Commented Mar 21, 2022 at 9:02
  • @Fanoflix I do want to change the passed prop after the first initialization. As I said, the passed prop componentOptions are being watched inside of the component to handle the state... Commented Mar 21, 2022 at 9:34

1 Answer 1

3

You may use <KeepAlive>, a built-in component available in both Vue2 (docs) and Vue3 (docs).

Basically it ensures that a component tagged with keep-alive will only be mounted once. So in your case, you can place the map wherever you want, and the Map will only be initialized once in its mounted hook.

If you need to utilize the moment that your Map gets "focused" or "activated" so to say, then you can utilize the activated and deactivated hooks.

Why you cannot use KeepAlive.

There is an obvious and logical limitation. As long as the parent is alive and mounted, the component's children that are being kept-alive will stay alive. But if the keep-alive component's parent gets unmounted, then all the children will be unmounted aswell even if they were being kept alive. This is all very obvious but I just felt like pointing it out.

Solution

So, in your use case, you want a component (the <Map> component) to be globally kept-alive after its first initialization. I suggest you cache the map element itself and store it in the store. Then on every <Map> component onBeforeMount (Composition API) or beforeMount (Options API) hook, manually check if the element is cached, if it is then insert the cached map from the store, otherwise initialize the map.

Sign up to request clarification or add additional context in comments.

1 Comment

Yeah that sounds like a valid approach. I can implement the component like a singleton. This doesn't sound like a too hacky way, I'll definitely try this out thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.