Skip to content

ObjectMap's marker useMemo never memoizes — getMapConfig runs unmemoized in the render body, so mapConfig has a fresh identity every render #5976

Description

@yinlianghui

Measured while implementing #5953 (PR #5975). Out of that card's scope — filing plainly for triage to grade.

What was measured

packages/plugin-map/src/ObjectMap.tsx:594 calls the config builder straight in the render body, unmemoized:

constmapConfig=getMapConfig(schema);

Every render therefore produces a new object identity. The marker transform names it in its dependency array (ObjectMap.tsx:737):

},[data,mapConfig,objectSchema]);

so that useMemo recomputes on every single render — it is a useMemo in spelling only. The recomputation walks all records through extractCoordinates and the display-name resolver. Downstream, filteredMarkers / clusteredData / markerBounds each depend on markers, so the invalidation cascades through the whole marker pipeline.

The sibling dataConfig immediately above it (ObjectMap.tsx:590) shows the shape the file already knows about, deliberately memoized on a deep-compare key:

constdataConfig=useMemo(()=>rawDataConfig,[JSON.stringify(rawDataConfig)]);

mapConfig got no equivalent.

Second-order cost inside getMapConfig itself

Because it is unmemoized, everything getMapConfig does happens per render, not per config change:

  • ObjectMapConfigSchema.safeParse(config) — a full zod parse on every render when a declared map block is present;
  • warnOnLegacyFilterMapConfig, warnOnTopLevelStyleUrl, warnOnShadowedFlatMapKeys.

The warn helpers are documented as warning "once per distinct stash" and carry their own dedupe, so this is a cost question rather than a log-spam one — but it is being paid on a per-render cadence.

Why this is worth its own card rather than a rider

It is not a correctness bug on any path I exercised: the recompute is wasteful, not wrong, and #5953's own change (adding objectSchema to that dep array, which it genuinely needs — the object definition arrives from an async fetch after first paint) neither causes nor worsens it. The fix is a different shape from that card's — memoize mapConfig, presumably on the same deep-compare key idiom the neighbouring dataConfig already uses — and touching it there would have been unreviewable scope creep inside a display-name fix.

Unassigned; filed plainly for triage to grade.


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions