Skip to content

[button group] Fix children remounting when siblings are added, removed or reordered - #49590

Open
kkatalz wants to merge 1 commit into
mui:masterfrom
kkatalz:fix/children-remounting-when-siblings-are
Open

kkatalz wants to merge 1 commit into
mui:masterfrom
kkatalz:fix/children-remounting-when-siblings-are

Conversation

@kkatalz

@kkatalz kkatalz commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Each child in ButtonGroup and ToggleButtonGroup is wrapped in a context provider with key={index}:

{validChildren.map((child, index) => {
return (
<ButtonGroupButtonContext.Provider
key={index}
value={getButtonPositionClassName(index)}
>
{child}
</ButtonGroupButtonContext.Provider>
);
})}

When a child before it comes or goes (like {showUndo && <Button>Undo</Button>}), the keys of the later children shift, so React unmounts and remounts them. A focused button drops focus to body and anything stateful inside it resets.

This uses key={child.key} instead. getValidReactChildren goes through React.Children.toArray, so every child already has a unique key. Position classes are still computed from the index on each render, so first/middle/last update as before.

Same one-line change in both components. Duplicate child keys now reach React as duplicate wrapper keys and it warns, which is what React.Children.map did before #38989 switched to the index.

I added a test per component, should keep a button mounted when the button before it is removed. On master both fail:

AssertionError: expected <button …(3)></button> to equal <button …(3)>…(1)</button>
AssertionError: expected <button …(5)></button> to equal <button …(5)>…(1)</button>

They pass with the fix. ButtonGroup.test.js 29 passed, ToggleButtonGroup.test.js 37 passed, and both specs pass in the chromium browser project (76 passed).

…ed or reordered

ButtonGroup and ToggleButtonGroup wrapped each child in a context provider keyed by its index, so when a
child before it was added or removed, a child's wrapper key changed and React remounted it: it lost focus
and its state. Key the wrapper by the child's own key, which React.Children.toArray always sets.
@code-infra-dashboard

code-infra-dashboard Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Deploy preview

https://deploy-preview-49590--material-ui.netlify.app/
QR code for https://deploy-preview-49590--material-ui.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/material 🔺+8B(0.00%) 🔺+3B(0.00%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/private-theming 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes

Performance

No regressions in 32 benchmarks · ran 5m 07s · details


Check out the code infra dashboard for more information about this PR.

@zannager zannager added the component: ButtonGroup The React component. label Oct 27, 2026
@zannager
zannager requested a review from silviuaavram October 27, 2026 14:30

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: ButtonGroup The React component.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants