Skip to content

Fix #5880: merge attributesThatAreSet delta on restore instead of replacing - #5881

Merged
BalusC merged 1 commit into
4.0from
issue_5880
Jul 21, 2026
Merged

Fix #5880: merge attributesThatAreSet delta on restore instead of replacing#5881
BalusC merged 1 commit into
4.0from
issue_5880

Conversation

@BalusC

@BalusC BalusC commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Fixes #5880.

ComponentStateHelper.restoreState treated a restored List as a wholesale replacement: it discarded both the default and delta entries for the key, then re-added only the saved delta. But the delta list is add-only — it carries only entries appended after markInitialState — so replacing dropped everything the tag handlers had just re-registered during buildView.

For attributesThatAreSet this meant that once a non-literal setValueExpression(name, ve) ran on a restored component (e.g. from a PostRestoreStateEvent listener), the poisoned delta wiped the pass-through attributes from the second postback onward. Any of styleClass, style, title, dir, lang, tabindex and the on* handlers stopped rendering and never recovered. styleClass became newly affected in 4.0.19/4.1.10 (#5753); the rest go back to #4269 (2021).

Fix

Merge the restored delta onto the list buildView already rebuilt this request, adding only entries not already present, matching the Map branch directly above it — which already merges. The List path serves exactly two keys (attributesThatAreSet and UIViewRoot.phaseListeners), both add-only with no overlap between the rebuilt default and the saved delta, so merge-with-dedup is lossless.

Test

UIComponentBaseTestCase#testAttributesThatAreSetMergeOnRestore reproduces the two-postback drop: a tag attribute registered pre-markInitialState, a non-literal binding added as the delta, saved, then restored onto a rebuilt list. Asserts the merged [styleClass, label]. Verified it fails against the old replace logic (expected <[styleClass, label]> but was <[label]>) and passes with the fix; full UIComponentBaseTestCase (43 tests) green.

🤖 Generated with Claude Code (Opus 4.8)

ComponentStateHelper.restoreState replaced the whole list-valued state
with the saved delta. The delta is add-only, carrying only entries
appended after markInitialState, so replacing dropped every entry the
tag handlers re-registered during buildView. attributesThatAreSet lost
those from the second postback on, so any pass-through attribute
(styleClass, style, title, dir, lang, tabindex, on*) stopped rendering
once a non-literal binding was set on the restored component, e.g. from
a PostRestoreStateEvent listener.

Merge the delta onto the rebuilt list instead, adding only entries not
already present, matching the Map branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@BalusC
BalusC merged commit 5dbac3f into 4.0 Jul 21, 2026
3 checks passed
@BalusC
BalusC deleted the issue_5880 branch July 21, 2026 11:05
@pizzi80

pizzi80 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Thanks for the fast fix!

@BalusC BalusC added this to the 4.0.21 milestone Jul 21, 2026
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