Repository navigation
Conversation
…he style `styledata` fires on every style change, such as addImage or setPaintProperty. Each event rendered every <Layer> and <Source> again, although the render only does work when the layer or source is missing, for example after a style swap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Medium impact] The PR appears safe to merge; no actionable new issues were found. SummaryReduces unnecessary renders after map style changes in both packages.
Reviews (2) · Last reviewed commit: "fix: re-render Source when the style has..." · Reviewed by Greptile |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Reviewed by Cursor Bugbot for commit fb15183. Configure here.
`<Source>` only renders its children once it has a source. When the style itself defines a source with the same id and loads after the first render, the `styledata` check found the source and skipped the render, so the children never mounted. Re-render whenever the source in the style is not the one the children were rendered with. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Disclaimer: Found and fixed with Clause Opus 5.5 High. Description also written by Claude and reviewed by me.
The problem
<Layer>and<Source>subscribe to the map'sstyledataevent and force a render on every event. MapLibre and Mapbox firestyledatafor every style change:map.addImage,map.setPaintProperty, a layer that another component adds. So with 120<Layer>s, every icon an app loads at runtime renders all 120 again, and every<Source>schedules a timer and a render.Those renders find the layer or source in the style and call
updateLayer/updateSourcewith unchanged props, which does nothing. The render only does useful work when the layer or source is missing from a loaded style, for example after a style swap.The change
The
styledatalistener now forces a render only when the layer (map.getLayer(id)) or source (map.getSource(id)) is missing, or the style is not loaded. The subscription and themap.style._loadedcheck stay as they are, so the timing of when sources and layers are added is unchanged (see the reverts in #2526).Applied to
layer.tsandsource.tsinreact-maplibreandreact-mapbox. I left themapbox-legacycopy inmodules/mainalone.Unchanged behaviour:
Source/Layertests).beforeIdis not in the style yet is still missing, so it renders again on the nextstyledataand is added then.New test in both packages:
addImage+setPaintPropertyno longer render<Source>or<Layer>, and a layer removed outside React is added again. It fails without the change.Measured, before and after
120 layers on 4 GeoJSON sources (10,000 features). Each
<Layer>and<Source>is wrapped in its ownReact.Profiler. Chrome 153 headless, Apple M4, maplibre-gl 6.14.0, React 18.3.1, median of 5 page loads per build.addImage, one per frame (100styledata)<Layer>renders<Source>renderssetPaintPropertyon one layer, one per frame<Layer>renders<Source>rendersmapStyleswapMain-thread script time is mostly MapLibre's own work, so on a fast machine its drop varies between runs: −1 % to −17 % over four runs. The saved render time grows with the number of layers and on slower devices. In an editor with 121 layers that adds marker images on demand, one load fired 51
styledataevents and 3,525<Layer>renders; with this change those renders are gone.Benchmark (before/after, runs with two checkouts of this repo): https://gist.github.com/tordans/964b42fdd89d39c1dfaf98d7419cf6a7
Validation: TypeScript build,
yarn lint, and all 86 node/headless tests pass.Note
Medium Risk
Touches map style sync for core Source/Layer components; behavior is narrowed but style swaps and missing-layer recovery must stay correct.
Overview
Stops
<Source>and<Layer>from re-rendering on every mapstyledataevent (e.g.addImage,setPaintProperty), which previously caused large apps with many layers to re-render unnecessarily.The
styledatahandler now callsforceUpdateonly when recovery is needed:<Layer>when the layer id is absent from a loaded style;<Source>when the source is missing or is not the same instance children were rendered with (sourceRef), including after a style swap or a colliding source id in the style. The same logic is applied inreact-mapboxandreact-maplibre.Tests assert zero profiler renders after unrelated style mutations, that externally removed layers are re-added, and that layers still mount when the initial style already defines the source.
Reviewed by Cursor Bugbot for commit 09816b1. Bugbot is set up for automated code reviews on this repo. Configure here.