🐛 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
🐛 Bug Report
The observerMap accessor created by
defineObservablePropertycallsassignObservablesbefore 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:
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:
For object and array values,
assignObservablescan 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:
Leave primitive handling simple unless profiling shows a real issue; primitive assignments are already cheap compared with object/array reprocessing.
Suggested acceptance coverage:
examples/ssr/webui-todo-appandexamples/ssr/chat-appfor per-frame or per-keystrokedeepMergeusage 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
defineObservablePropertyor observerMap assignment reprocessing reports and did not find a duplicate.🌍 Your Environment
@microsoft/fast-htmlafter fix: replace arrays during observerMap deepMerge #7559