Skip to content

perf: memoize render-path work in item renderer and root#460

Merged
paul-phan merged 2 commits into
mainfrom
perf/render-path-memoization
Jun 11, 2026
Merged

perf: memoize render-path work in item renderer and root#460
paul-phan merged 2 commits into
mainfrom
perf/render-path-memoization

Conversation

@paul-phan

Copy link
Copy Markdown
Member

Three render-mechanism costs eliminated, none behavior-changing:

  • ItemComponent re-ran replaceContentDataConnectorsDeep on every render
    of every item — a deep placeholder scan (plus deep clone when
    connectors exist) over the full item data including loaderData
    payloads (~0.5ms per full-page re-render pass with realistic
    product-section data on M4, ~5x that on low-end mobile). Now memoized
    per snapshot: getSnapShot() is identity-stable, and context-only
    updates already swap the store reference via setData({}) in
    syncReusedInstance, so [data, context.dataContext] is a sufficient
    dependency key.

  • RenderRoot walked and re-registered the full component list on every
    render; registration is idempotent but the spread + loop ran each
    time. Component arrays are module-level constants — register each
    list identity once via WeakSet.

  • WeaverseHydrogenRoot rebuilt the route-keyed data context object on
    every render; now memoized on matches. Component.displayName is also
    assigned only when it actually changes instead of every render.

Three render-mechanism costs eliminated, none behavior-changing:

- ItemComponent re-ran replaceContentDataConnectorsDeep on every render
  of every item — a deep placeholder scan (plus deep clone when
  connectors exist) over the full item data including loaderData
  payloads (~0.5ms per full-page re-render pass with realistic
  product-section data on M4, ~5x that on low-end mobile). Now memoized
  per snapshot: getSnapShot() is identity-stable, and context-only
  updates already swap the store reference via setData({}) in
  syncReusedInstance, so [data, context.dataContext] is a sufficient
  dependency key.

- RenderRoot walked and re-registered the full component list on every
  render; registration is idempotent but the spread + loop ran each
  time. Component arrays are module-level constants — register each
  list identity once via WeakSet.

- WeaverseHydrogenRoot rebuilt the route-keyed data context object on
  every render; now memoized on matches. Component.displayName is also
  assigned only when it actually changes instead of every render.
@paul-phan paul-phan merged commit 43d3f65 into main Jun 11, 2026
9 checks passed
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.

1 participant