Frontend Map Rendering — Mapbox e Performance
Renderizar mapas com milhares de markers. Clustering, virtualização de pins, WebGL e lazy loading de tiles.
Definição
Maps no frontend: Mapbox GL JS (WebGL, performático, customizável), Google Maps (mais popular, mais pesado), Leaflet (simples, menos features). WebGL = renderização na GPU, suporta milhares de markers a 60fps via GeoJSON layers. Clustering: agrupar markers próximos em zoom out. Tile loading lazy: carrega tiles conforme o viewport muda. Crítico para apps de delivery, ride-sharing, logística.
Problema → Solução
Problema
Usar `<Marker>` do React para 500 pins no mapa = 500 elementos no DOM = jank em zoom/pan. Cada Marker é um overlay HTML, não renderizado pelo WebGL. Resultado: mapa congela.Solução
Para muitos markers estáticos: GeoJSON Layer no Mapbox (renderizado via WebGL, suporta milhões). Para markers interativos com React: virtualizar, mostrar só os visíveis no viewport. Clustering com supercluster ou Mapbox cluster source. Para 1-10 markers interativos: `<Marker>` HTML está bem. Regra: > 50 markers → GeoJSON/WebGL.Dica de entrevista
Tip
Lazy loading do bundle do mapa (Mapbox GL JS tem ~250kb gz). Usar dynamic import ou Next.js `dynamic(() => import(...), { ssr: false })`. O mapa precisa de um container com height definido — sem isso, Mapbox renderiza com 0px de altura. Invalidar o mapa com `map.resize()` quando o container muda de tamanho (sidebar abre/fecha, fullscreen).Código
// react-map-gl (wrapper React do Mapbox) com GeoJSON source
import Map, { Source, Layer, Marker } from 'react-map-gl'
function RideMap({ drivers, surgeZones }: MapProps) {
const mapRef = useRef<MapRef>(null)
// GeoJSON para muitos drivers (renderizado via WebGL)
const driversGeoJSON: GeoJSON.FeatureCollection = useMemo(() => ({
type: 'FeatureCollection',
features: drivers.map(d => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [d.lng, d.lat] },
properties: { id: d.id, status: d.status }
}))
}), [drivers])
// Atualizar posição sem re-render React
const updateDriverPosition = useCallback((driverId: string, pos: Position) => {
const map = mapRef.current?.getMap()
const source = map?.getSource('drivers') as GeoJSON.GeoJSONSource
// Atualiza direto no Mapbox (GPU), sem useState
source?.setData(driversGeoJSON)
}, [driversGeoJSON])
return (
<Map
ref={mapRef}
mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_TOKEN}
initialViewState={{ longitude: -46.63, latitude: -23.55, zoom: 13 }}
style={{ width: '100%', height: '500px' }}
>
{/* Drivers como WebGL layer — suporta milhares */}
<Source id="drivers" type="geojson" data={driversGeoJSON} cluster clusterRadius={50}>
<Layer id="clusters" type="circle" filter={['has', 'point_count']}
paint={{ 'circle-color': '#3fb6f0', 'circle-radius': 20 }} />
<Layer id="driver-points" type="symbol" filter={['!', ['has', 'point_count']]}
layout={{ 'icon-image': 'car-icon' }} />
</Source>
{/* Surge zones como polígono */}
<Source id="surge" type="geojson" data={surgeZones}>
<Layer id="surge-fill" type="fill"
paint={{ 'fill-color': '#fb923c', 'fill-opacity': 0.2 }} />
</Source>
</Map>
)
}Perguntas de entrevista
Q.Como atualizar a posição de um motorista em tempo real no mapa sem re-renderizar o React?▾
A.Guardar referência ao objeto Marker ou Layer do Mapbox e atualizar via API imperativa do Mapbox (sem React re-render). `markerRef.current.setLngLat([lng, lat])` para Markers. Para Source GeoJSON: `map.getSource('drivers').setData(newGeoJSON)` — Mapbox só atualiza os dados na GPU, sem tocar no DOM. Isso mantém animações suaves a 60fps.
Q.Como implementar clustering eficiente?▾
A.Usar Mapbox Cluster Source: `type: 'geojson', cluster: true, clusterRadius: 50`. Mapbox calcula clusters no worker thread. Para interação: ao clicar num cluster, zoom para mostrar os items individuais com `map.flyTo`. Para clusters customizados em React: supercluster library + calcular clusters no useMemo quando viewport muda (bounds + zoom).
Q.Como lidar com geofences e polígonos no mapa?▾
A.Mapbox Fill Layer para polígonos (surge pricing zones, delivery areas). GeoJSON Polygon ou MultiPolygon como source. Para interatividade: `map.on('click', 'geofence-layer', handler)` para detectar clicks. Para verificar se um ponto está dentro de um polígono no cliente: turf.js `booleanPointInPolygon()`. Para polígonos dinâmicos (desenhados pelo usuário): mapbox-gl-draw plugin.
Termos relacionados
120/122← →