Claim the useId name space for every auto named ViewTransition (#33200)
This is a partial revert of #33094. It's true that we don't need the server and client ViewTransition names to line up. However the server does need to be able to generate deterministic names for itself. The cheapest way to do that is using the useId algorithm. When it's used by the server, the client needs to also materialize an ID even if it doesn't use it.
Sebastian Markbåge committed
May 14, 2025 at 17:52 UTC
96eb84e493c4ff2c280990659057164c0f16bbb8
2 files changed
+23
-1
packages/react-reconciler/src/ReactFiberBeginWork.js
+6
@@ -3543,6 +3543,12 @@ function updateViewTransition(
3543
current === null
3544
? ViewTransitionNamedMount | ViewTransitionNamedStatic
3545
: ViewTransitionNamedStatic;
3546
+ } else {
3547
+ // The server may have used useId to auto-assign a generated name for this boundary.
3548
+ // We push a materialization to ensure child ids line up with the server.
3549
+ if (getIsHydrating()) {
3550
+ pushMaterializedTreeId(workInProgress);
3551
+ }
3552
}
3553
if (__DEV__) {
3554
// $FlowFixMe[prop-missing]
packages/react-server/src/ReactFizzServer.js
+17
-1
@@ -2273,7 +2273,23 @@ function renderViewTransition(
2273
) {
2274
const prevKeyPath = task.keyPath;
2275
task.keyPath = keyPath;
2276
- renderNodeDestructive(request, task, props.children, -1);
2276
+ if (props.name != null && props.name !== 'auto') {
2277
+ renderNodeDestructive(request, task, props.children, -1);
2278
+ } else {
2279
+ // This will be auto-assigned a name which claims a "useId" slot.
2280
+ // This component materialized an id. We treat this as its own level, with
2281
+ // a single "child" slot.
2282
+ const prevTreeContext = task.treeContext;
2283
+ const totalChildren = 1;
2284
+ const index = 0;
2285
+ // Modify the id context. Because we'll need to reset this if something
2286
+ // suspends or errors, we'll use the non-destructive render path.
2287
+ task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
2288
+ renderNode(request, task, props.children, -1);
2289
+ // Like the other contexts, this does not need to be in a finally block
2290
+ // because renderNode takes care of unwinding the stack.
2291
+ task.treeContext = prevTreeContext;
2292
+ }
2293
task.keyPath = prevKeyPath;
2294
}
2295