Skip to content

fix: prevent duplicate layers when re-opening a map from Data Visualizer - #3746

Draft
karolinelien wants to merge 5 commits into
masterfrom
fix/open-as-map-duplicate-layers
Draft

fix: prevent duplicate layers when re-opening a map from Data Visualizer#3746
karolinelien wants to merge 5 commits into
masterfrom
fix/open-as-map-duplicate-layers

Conversation

@karolinelien

@karolinelien karolinelien commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Implements DHIS2-XXXX

Description

Reproduction: open a single-layer map in Maps, use the layer's "Open in Data Visualizer app" menu action, then in Data Visualizer click "Open as map" without making any changes. The map fails to render and the layer list shows two duplicate copies of the same layer.

Two independent bugs in OpenAsMapDialog combine to produce that:

1. Duplicate layers. The dialog auto-added the layer (for the single-data-item / Earth Engine cases, where no user picker is needed) by dispatching directly from the render body. That path is async, and any re-render while it was in flight re-invoked the same dispatch, adding the layer twice with two different generated ids. Nothing downstream could catch it: LAYER_ADD assigns id: generateUid() and only dedupes on action.payload.id, which the payload never carries.

This is a long-standing latent bug from #3009 (the class-to-function refactor of this component) — it just needed an extra re-render window to manifest.

Fix: move the auto-add into a useEffect guarded by a ref so it runs once per mount.

2. The layer was added switched off, which is why the map rendered empty. isVisible was computed as dataId === allDataDimensions[len - 1] — an id string compared against a dimension object, so it was always false, and the reducer's isVisible ?? true fallback can't rescue an explicit false. Fix: compare against the last id in the loop.

Also fixed along the way:

  • Cancel passed the clearAnalyticalObject action creator straight to onClick, so it was invoked with the click event and never dispatched — the button did nothing.
  • Proceed could be double-clicked into a duplicate add; it is now guarded and disabled while adding.
  • The Earth Engine path dereferenced layerSource.defaultAggregations before null-checking layerSource.
  • clearAnalyticalObject() now always runs via finally, so a rejected legend-set fetch no longer leaves the dialog stuck.
  • The default data-item selection is filled in if the analytical object resolves after mount, instead of leaving [undefined] selected with Proceed enabled.

AI Assisted


Quality checklist

Add N/A to items that are not applicable.

  • Dashboard tested
  • Cypress and/or Jest tests added/updated
  • Docs added: N/A
  • d2-ci dependencies replaced: N/A
  • Tester approved (name)

ToDos

  • N/A

Known issues

  • N/A

Screenshots

N/A

OpenAsMapDialog auto-added single-item/Earth Engine layers by dispatching
directly from the render body. Since that path is async, extra re-renders
while it was in flight (e.g. while useSetting/legend fetches resolved)
re-triggered the dispatch, producing duplicate layers and leaving the map
in a broken state. Move the auto-add into an effect guarded to run once.

AI Assisted
@dhis2-bot

Copy link
Copy Markdown
Contributor

🚀 Deployed on https://pr-3746.maps.netlify.dhis2.org

@dhis2-bot
dhis2-bot temporarily deployed to netlify July 30, 2026 16:28 Inactive
@karolinelien
karolinelien marked this pull request as ready for review July 30, 2026 16:33
- Guard the manual Proceed button against double-click duplicate dispatch
- Fix Cancel dispatching the action creator instead of dispatch()
- Ensure clearAnalyticalObject always runs via try/finally, even if the async add throws
- Strengthen tests: StrictMode double-invocation, Proceed double-click, Cancel dispatch
- Trim useEffect comment to one line per repo convention

AI Assisted
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 30, 2026 17:01 Inactive
The layer-visibility flag compared a data item id against a dimension
object, so every layer created from an analytical object was added
switched off -- the other half of the "map fails to render" symptom.

Also address review feedback on the duplicate-layer fix:

- Collapse hasAutoAdded/isAddingRef into a single hasAddedRef; adding
  always ends in clearAnalyticalObject(), which unmounts the dialog, so
  the guard never needs resetting
- Disable Proceed while adding, so a double-click gets feedback instead
  of a silent no-op
- Null-check the earth engine layer source before dereferencing it, and
  drop the inert try/finally around that synchronous path
- Preselect the first data item if the analytical object resolves after
  mount, instead of leaving [undefined] selected
- Explain why the effect deps are load-bearing
- Cover the earth engine path and layer visibility in the tests

AI Assisted

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 30, 2026 20:29 Inactive
DHIS2-15762 is the hash-routing feature that introduced the latent bug,
not a ticket tracking it.

AI Assisted

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@karolinelien
karolinelien marked this pull request as draft July 30, 2026 20:30
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 30, 2026 20:32 Inactive
Resolves the SonarCloud javascript:S7755 code smell and matches the
existing idiom in thematicLoader/classify.

AI Assisted

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@dhis2-bot
dhis2-bot temporarily deployed to netlify July 30, 2026 20:39 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants