Skip to content

[Transition] Mount child in same commit when opening from unmounted#48649

Merged
siriwatknp merged 3 commits into
mui:masterfrom
siriwatknp:fix/transition-mount-on-open-48637
Jun 11, 2026
Merged

[Transition] Mount child in same commit when opening from unmounted#48649
siriwatknp merged 3 commits into
mui:masterfrom
siriwatknp:fix/transition-mount-on-open-48637

Conversation

@siriwatknp

Copy link
Copy Markdown
Member

closes #48637

Summary

Fixes the VerticalLinearStepper demo crash introduced by the custom Transition component (#48325).

Root cause

When opening from unmounted, the new internal/Transition moved the child from unmounted → exited inside a layout effect, adding a commit where the child is still null. react-transition-group derived that status from props during render, so the child existed in the same commit in flipped true.

The stepper demo focuses a button synchronously in a passive effect right after activeStep changes. With the extra commit, the previous step's button is already unmounted and the new one isn't mounted yet, so continueButtonRef.current is null and .focus() throws.

This is a silent behavior regression for any consumer that reads a ref (focus management, measurement, scroll-into-view) right after in toggles — not just this demo.

Fix

Move the unmounted → exited transition to a guarded render-phase update so the child is committed (and its ref attached) in the same commit in turns true, matching react-transition-group. The redundant unmounted branch is removed from the reconcile effect. No demo change needed.

Testing

Added a regression test mirroring the demo's exact pattern (a parent passive effect reads the child ref right after in flips). It fails (null) without the fix and passes with it.

Verified green: Transition, Collapse, StepContent, Fade, Grow, Zoom, Slide, Accordion, Snackbar, Tooltip, Dialog, Drawer, Menu, Popover, SpeedDial, TouchRipple, Ripple.

When opening from unmounted, the child was mounted in a layout effect, adding a commit where it was still null. Consumers reading a ref right after `in` flips (e.g. focus management in the vertical stepper demo) crashed. Restore react-transition-group's behavior of deriving the status during render so the child and its ref are present in the same commit.
@code-infra-dashboard

code-infra-dashboard Bot commented Jun 10, 2026

Copy link
Copy Markdown

Deploy preview

https://deploy-preview-48649--material-ui.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/material 🔺+4B(0.00%) 🔺+1B(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


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

@siriwatknp siriwatknp requested a review from mj12albert June 10, 2026 11:47
Tighten regression test: compare ref to mounted node instead of !== null,
and assert onExited stays silent during the intermediate exited status.
@mj12albert

Copy link
Copy Markdown
Member

This seems a better way than mine #48646

@mj12albert mj12albert left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@siriwatknp siriwatknp changed the title [transitions] Mount child in same commit when opening from unmounted [Transition] Mount child in same commit when opening from unmounted Jun 11, 2026
@siriwatknp siriwatknp merged commit aa5e12d into mui:master Jun 11, 2026
18 checks passed
@zannager zannager added the scope: transitions Changes related to the transitions. label Jun 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: transitions Changes related to the transitions.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[docs] Demo VerticalLinearStepper crashes

3 participants