Skip to content

fix: avoid same-reference observerMap assignment reprocessing in fast-html #7581

Description

@janechu

🐛 Bug Report

The observerMap accessor created by defineObservableProperty calls assignObservables before checking whether the assigned value is already the same reference stored in the backing field. For object and array values, this can walk the schema and reprocess nested objects/arrays even when the assignment was intended to be a no-op.

This is a follow-up hardening issue for #7559.

💻 Repro or Code Sample

Conceptual repro:

const orders = element.user.orders;

element.user.orders = orders;

Assigning the same observerMap-managed array reference back to the property should not re-run schema processing, proxy installation, or notification work.

🤔 Expected Behavior

When an observerMap-generated setter receives the same reference currently stored in its backing field, it should return before doing any expensive wrapping or notification work.

😯 Current Behavior

The setter computes:

const oldValue = source[field];
const newValue = assignObservables(schema, rootSchema, value, target, rootProperty);

if (oldValue !== newValue) {
    source[field] = newValue;
    Observable.notify(source, key);
}

For object and array values, assignObservables can walk the schema and reprocess nested structures before the same-reference no-op is known.

💁 Possible Solution

Add a guard at the start of the generated setter:

if (value === source[field]) {
    return;
}

Leave primitive handling simple unless profiling shows a real issue; primitive assignments are already cheap compared with object/array reprocessing.

Suggested acceptance coverage:

  • A focused test that assigns the same observerMap-managed object/array reference back to a generated observable property and verifies no notification fires.
  • Benchmark or profiling numbers for a representative nested observerMap tree, if available.
  • Audit examples/ssr/webui-todo-app and examples/ssr/chat-app for per-frame or per-keystroke deepMerge usage and confirm no visible regression.

🔦 Context

PR #7559 added a custom accessor path so replacement arrays are processed through observerMap and remain observable. This follow-up keeps that behavior while avoiding unnecessary work for same-reference assignments.

Searched existing issues for overlapping defineObservableProperty or observerMap assignment reprocessing reports and did not find a duplicate.

🌍 Your Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    improvementA non-feature-adding improvementstatus:triageNew Issue - needs triage

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions