Repository: facebook/react
Pull request: Remove useId semantics from View Transition name generation

Originally I thought it was important that SSR used the same View Transition name as the client so that the Fizz runtime could emit those names and then the client could pick up and take over. However, I no longer believe that approach is feasible. Instead, the names can be generated only during that particular animation.

Therefore we can simplify the auto name assignment to not have to consider the hydration.


Changed files:
filename: packages/react-reconciler/src/ReactFiberBeginWork.js
patch: @@ -36,8 +36,6 @@ import type {
   OffscreenQueue,
   OffscreenInstance,
 } from './ReactFiberOffscreenComponent';
-import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
-import {assignViewTransitionAutoName} from './ReactFiberViewTransitionComponent';
 import type {
   Cache,
   CacheComponentState,
@@ -3538,24 +3536,13 @@ function updateViewTransition(
   renderLanes: Lanes,
 ) {
   const pendingProps: ViewTransitionProps = workInProgress.pendingProps;
-  const instance: ViewTransitionState = workInProgress.stateNode;
   if (pendingProps.name != null && pendingProps.name !== 'auto') {
     // Explicitly named boundary. We track it so that we can pair it up with another explicit
     // boundary if we get deleted.
     workInProgress.flags |=
       current === null
         ? ViewTransitionNamedMount | ViewTransitionNamedStatic
         : ViewTransitionNamedStatic;
-  } else {
-    // Assign an auto generated name using the useId algorthim if an explicit one is not provided.
-    // We don't need the name yet but we do it here to allow hydration state to be used.
-    // We might end up needing these to line up if we want to Transition from dehydrated fallback
-    // to client rendered content. If we don't end up using that we could just assign an incremeting
-    // counter in the commit phase instead.
-    assignViewTransitionAutoName(pendingProps, instance);
-    if (getIsHydrating()) {
-      pushMaterializedTreeId(workInProgress);
-    }
   }
   if (__DEV__) {
     // $FlowFixMe[prop-missing]

filename: packages/react-reconciler/src/ReactFiberViewTransitionComponent.js
patch: @@ -12,14 +12,10 @@ import type {FiberRoot} from './ReactInternalTypes';
 import type {ViewTransitionInstance, Instance} from './ReactFiberConfig';
 
 import {
-  getWorkInProgressRoot,
+  getCommittingRoot,
   getPendingTransitionTypes,
 } from './ReactFiberWorkLoop';
 
-import {getIsHydrating} from './ReactFiberHydrationContext';
-
-import {getTreeId} from './ReactFiberTreeContext';
-
 export type ViewTransitionState = {
   autoName: null | string, // the view-transition-name to use when an explicit one is not specified
   paired: null | ViewTransitionState, // a temporary state during the commit phase if we have paired this with another instance
@@ -29,47 +25,27 @@ export type ViewTransitionState = {
 
 let globalClientIdCounter: number = 0;
 
-export function assignViewTransitionAutoName(
+export function getViewTransitionName(
   props: ViewTransitionProps,
   instance: ViewTransitionState,
 ): string {
+  if (props.name != null && props.name !== 'auto') {
+    return props.name;
+  }
   if (instance.autoName !== null) {
     return instance.autoName;
   }
 
-  const root = ((getWorkInProgressRoot(): any): FiberRoot);
+  // We assume we always call this in the commit phase.
+  const root = ((getCommittingRoot(): any): FiberRoot);
   const identifierPrefix = root.identifierPrefix;
-
-  let name;
-  if (getIsHydrating()) {
-    const treeId = getTreeId();
-    // Use a captial R prefix for server-generated ids.
-    name = '\u00AB' + identifierPrefix + 'T' + treeId + '\u00BB';
-  } else {
-    // Use a lowercase r prefix for client-generated ids.
-    const globalClientId = globalClientIdCounter++;
-    name =
-      '\u00AB' +
-      identifierPrefix +
-      't' +
-      globalClientId.toString(32) +
-      '\u00BB';
-  }
+  const globalClientId = globalClientIdCounter++;
+  const name =
+    '\u00AB' + identifierPrefix + 't' + globalClientId.toString(32) + '\u00BB';
   instance.autoName = name;
   return name;
 }
 
-export function getViewTransitionName(
-  props: ViewTransitionProps,
-  instance: ViewTransitionState,
-): string {
-  if (props.name != null && props.name !== 'auto') {
-    return props.name;
-  }
-  // We should have assigned a name by now.
-  return (instance.autoName: any);
-}
-
 function getClassNameByType(classByType: ?ViewTransitionClass): ?string {
   if (classByType == null || typeof classByType === 'string') {
     return classByType;

filename: packages/react-reconciler/src/ReactFiberWorkLoop.js
patch: @@ -700,6 +700,10 @@ export function getWorkInProgressRoot(): FiberRoot | null {
   return workInProgressRoot;
 }
 
+export function getCommittingRoot(): FiberRoot | null {
+  return pendingEffectsRoot;
+}
+
 export function getWorkInProgressRootRenderLanes(): Lanes {
   return workInProgressRootRenderLanes;
 }

filename: packages/react-server/src/ReactFizzServer.js
patch: @@ -2274,23 +2274,7 @@ function renderViewTransition(
 ) {
   const prevKeyPath = task.keyPath;
   task.keyPath = keyPath;
-  if (props.name != null && props.name !== 'auto') {
-    renderNodeDestructive(request, task, props.children, -1);
-  } else {
-    // This will be auto-assigned a name which claims a "useId" slot.
-    // This component materialized an id. We treat this as its own level, with
-    // a single "child" slot.
-    const prevTreeContext = task.treeContext;
-    const totalChildren = 1;
-    const index = 0;
-    // Modify the id context. Because we'll need to reset this if something
-    // suspends or errors, we'll use the non-destructive render path.
-    task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
-    renderNode(request, task, props.children, -1);
-    // Like the other contexts, this does not need to be in a finally block
-    // because renderNode takes care of unwinding the stack.
-    task.treeContext = prevTreeContext;
-  }
+  renderNodeDestructive(request, task, props.children, -1);
   task.keyPath = prevKeyPath;
 }