@samitouri / QOS-React-1 / commits / 96eb84e493

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