I am trying to learn how to use 'react-map-gl/maplibre' layer hide/show functionality and the issue I am facing is with updating both layer and paint properties of my map instance.
I am very new to the web map apps and sure miss something very basic here;(
Is that even possible to update a map properties without passing down the whole style object on each change?
I assume mapRef.current is an API of an initialized Map instance which allows control of both symbols and layers, but why is it missing set methods?
Why non-react examples can perfectly use sets?
Here is the detailed issue:
Calling to setLayoutProperty when the map is loaded fails with
"setLayoutProperty is not a function"
On the other hand, getLayoutProperty works fine and can retrieve "visibility" property.
Here is how I define my map :
"use client";
import { useCallback, useRef, useState } from "react";
import { Map, Source, Layer, type MapRef } from "react-map-gl/maplibre";
export default function MapComponent() {
// I suspect that this is where I am presumably wrong.
// I assume that 'maplibregl' type Map is the same as 'react-map-gl/maplibre' Map component ref={mapRef}
// This is where I try to declare my mapRef following answer
// from here https://stackoverflow.com/questions/68368898/typescript-type-for-mapboxgljs-react-ref-hook
// const mapRef = useRef<maplibregl.Map | null>(null);
//
// trying other approach, still same error, but now it is clear that those methods do not exist from intellisense
const mapRef = useRef<MapRef>(null);
const sourceId = "places";
const circleLayerId = "circles";
const symbolLayerId = "symbols";
const visibility = "visibility";
const circleRadius = "circle-radius";
return (
<>
<Map
reuseMaps
{...viewport}
ref={mapRef}
style={{ width: "100vw", height: "100vh", display: "flex" }}
mapStyle={
"https://api.maptiler.com/maps/basic-v2/style.json?key=xxxxxxxx"
}
>
<Source
id={sourceId}
type="geojson"
maxzoom={15}
data="https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson"
>
<Layer
{...{
source: sourceId,
id: circleLayerId,
type: "circle",
paint: {
"circle-color": "#ff0000",
"circle-radius": 12,
"circle-stroke-width": 1,
"circle-stroke-color": "#000",
},
layout: {
visibility: "visible",
},
}}
/>
<Layer
{...{
id: symbolLayerId,
type: "symbol",
source: sourceId,
layout: {
visibility: "visible",
"text-allow-overlap": true,
"text-font": ["Arial Italic"],
"text-field": ["get", "mag"],
"text-size": [
"interpolate",
["linear"],
["zoom"],
// zoom is 5 (or less)
5,
12,
// zoom is 10 (or greater)
10,
11,
],
"text-anchor": "center",
// "text-offset": [0, -2],
},
}}
/>
</Source>
</Map>
<div
style={{
position: "absolute",
display: "flex",
zIndex: 100,
width: "90%",
justifyContent: "space-between",
padding: 10,
}}
>
<ul>
<li>
<span>
<button
onClick={(e) => {
handleVisibility(symbolLayerId, false);
}}
>
Hide "Symbols" using visibility
</button>
</span>
</li>
<li>
<span>
<button
onClick={(e) => {
handleVisibility(circleLayerId, false);
}}
>
Hide "Circles" using visibility
</button>
</span>
</li>
<li>
<span>
<button onClick={(e) => handleRadius(circleLayerId, 0)}>
Scale "Circles" radius to 0
</button>
</span>
</li>
</ul>
</div>
</>
);
}
Reproduction in sandbox (just use your own token for tiles) https://codesandbox.io/p/github/muxalko/smokemap-ui/main
Instead of setLayoutProperty method, use state to control any desired property inside LayoutProps. For example:
Lets try create a state for symbol layer visibility. First, you would need to define a state:
Then, use the defined state variable in Layer props:
Now, we can control the visibility prop by changing
symbolLayerVisibilitystate with any of the events like onClick, onHover, onChange , etc...Using this approach, I was able to successfully hide/show layers both by layer visibility and circle radius as well as control other params as colors and text size.
Sometimes, when you have a lot of layers, it becomes hard to manage states in separate useState hooks. This is how I manage param for multiple layers with a Map dictionary.
Define a map dict to hold values:
Then, to use it, we just iterate over the array of Layers and fetch values from our state variables.
For example, component that render Layers may look like:
Here is a little demo on YouTube: Updating map layers visibility in react-map-gl
Codesandbox with full solution