Skip to content

Commit d8e4817

Browse files
committed
View Transition Refs (facebook#32038)
This adds refs to View Transition that can resolve to an instance of: ```js type ViewTransitionRef = { name: string, group: Animatable, imagePair: Animatable, old: Animatable, new: Animatable, } ``` Animatable is a type that has `animate(keyframes, options)` and `getAnimations()` on it. It's the interface that exists on Element that lets you start animations on it. These ones are like that but for the four pseudo-elements created by the view transition. If a name changes, then a new ref is created. That way if you hold onto a ref during an exit animation spawned by the name change, you can keep calling functions on it. It will keep referring to the old name rather than the new name. This allows imperative control over the animations instead of using CSS for this. ```js const viewTransition = ref.current; const groupAnimation = viewTransition.group.animate(keyframes, options); const imagePairAnimation = viewTransition.imagePair.animate(keyframes, options); const oldAnimation = viewTransition.old.animate(keyframes, options); const newAnimation = viewTransition.new.animate(keyframes, options); ``` The downside of using this API is that it doesn't work with SSR so for SSR rendered animations they'll fallback to the CSS. You could use this for progressive enhancement though. Note: In this PR the ref only controls one DOM node child but there can be more than one DOM node in the ViewTransition fragment and they are just left to their defaults. We could try something like making the `animate()` function apply to multiple children but that could lead to some weird consequences and the return value would be difficult to merge. We could try to maintain an array of Animatable that updates with how ever many things are currently animating but that makes the API more complicated to use for the simple case. Conceptually this should be like a fragment so we would ideally combine the multiple children into a single isolate if we could. Maybe one day the same name could be applied to multiple children to create a single isolate. For now I think I'll just leave it like this and you're really expect to just use it with one DOM node. If you have more than one they just get the default animations from CSS. Using this is a little tricky due timing. In this fixture I just use a layout effect plus rAF to get into the right timing after the startViewTransition is ready. In the future I'll add an event that fires when View Transitions heuristics fire with the right timing. DiffTrain build for [0bf1f39](facebook@0bf1f39)
1 parent a6512b7 commit d8e4817

23 files changed

+294
-277
lines changed

compiled-rn/VERSION_NATIVE_FB

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
19.1.0-native-fb-056073de-20250109
1+
19.1.0-native-fb-0bf1f39e-20250110

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/react-dom/cjs/ReactDOM-dev.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<43825cc79ad4b8b2491f4fe4c4cea764>>
10+
* @generated SignedSource<<b57d260398996604ce144f77869f967c>>
1111
*/
1212

1313
"use strict";
@@ -420,5 +420,5 @@ __DEV__ &&
420420
exports.useFormStatus = function () {
421421
return resolveDispatcher().useHostTransitionStatus();
422422
};
423-
exports.version = "19.1.0-native-fb-056073de-20250109";
423+
exports.version = "19.1.0-native-fb-0bf1f39e-20250110";
424424
})();

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/react-dom/cjs/ReactDOM-prod.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<9605c0b9e6866251811176e83b991a62>>
10+
* @generated SignedSource<<d68d8f9b1bea78f147f97a6fc1d03018>>
1111
*/
1212

1313
"use strict";
@@ -203,4 +203,4 @@ exports.useFormState = function (action, initialState, permalink) {
203203
exports.useFormStatus = function () {
204204
return ReactSharedInternals.H.useHostTransitionStatus();
205205
};
206-
exports.version = "19.1.0-native-fb-056073de-20250109";
206+
exports.version = "19.1.0-native-fb-0bf1f39e-20250110";

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/react-dom/cjs/ReactDOM-profiling.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<9605c0b9e6866251811176e83b991a62>>
10+
* @generated SignedSource<<d68d8f9b1bea78f147f97a6fc1d03018>>
1111
*/
1212

1313
"use strict";
@@ -203,4 +203,4 @@ exports.useFormState = function (action, initialState, permalink) {
203203
exports.useFormStatus = function () {
204204
return ReactSharedInternals.H.useHostTransitionStatus();
205205
};
206-
exports.version = "19.1.0-native-fb-056073de-20250109";
206+
exports.version = "19.1.0-native-fb-0bf1f39e-20250110";

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/react-dom/cjs/ReactDOMClient-dev.js

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<b215c441e5306b880f26cec96cb108af>>
10+
* @generated SignedSource<<babb8c61ae7fdb8ad522282c367e88ee>>
1111
*/
1212

1313
/*
@@ -11646,15 +11646,24 @@ __DEV__ &&
1164611646
function commitAttachRef(finishedWork) {
1164711647
var ref = finishedWork.ref;
1164811648
if (null !== ref) {
11649-
var instance = finishedWork.stateNode;
11649+
switch (finishedWork.tag) {
11650+
case 26:
11651+
case 27:
11652+
case 5:
11653+
var instanceToUse = finishedWork.stateNode;
11654+
break;
11655+
default:
11656+
instanceToUse = finishedWork.stateNode;
11657+
}
1165011658
if ("function" === typeof ref)
1165111659
if (shouldProfile(finishedWork))
1165211660
try {
11653-
startEffectTimer(), (finishedWork.refCleanup = ref(instance));
11661+
startEffectTimer(),
11662+
(finishedWork.refCleanup = ref(instanceToUse));
1165411663
} finally {
1165511664
recordEffectDuration();
1165611665
}
11657-
else finishedWork.refCleanup = ref(instance);
11666+
else finishedWork.refCleanup = ref(instanceToUse);
1165811667
else
1165911668
"string" === typeof ref
1166011669
? error$jscomp$0("String refs are no longer supported.")
@@ -11663,7 +11672,7 @@ __DEV__ &&
1166311672
"Unexpected ref object provided for %s. Use either a ref-setter function or React.createRef().",
1166411673
getComponentNameFromFiber(finishedWork)
1166511674
),
11666-
(ref.current = instance);
11675+
(ref.current = instanceToUse);
1166711676
}
1166811677
}
1166911678
function safelyAttachRef(current, nearestMountedAncestor) {
@@ -25942,11 +25951,11 @@ __DEV__ &&
2594225951
};
2594325952
(function () {
2594425953
var isomorphicReactPackageVersion = React.version;
25945-
if ("19.1.0-native-fb-056073de-20250109" !== isomorphicReactPackageVersion)
25954+
if ("19.1.0-native-fb-0bf1f39e-20250110" !== isomorphicReactPackageVersion)
2594625955
throw Error(
2594725956
'Incompatible React versions: The "react" and "react-dom" packages must have the exact same version. Instead got:\n - react: ' +
2594825957
(isomorphicReactPackageVersion +
25949-
"\n - react-dom: 19.1.0-native-fb-056073de-20250109\nLearn more: https://react.dev/warnings/version-mismatch")
25958+
"\n - react-dom: 19.1.0-native-fb-0bf1f39e-20250110\nLearn more: https://react.dev/warnings/version-mismatch")
2595025959
);
2595125960
})();
2595225961
("function" === typeof Map &&
@@ -25983,10 +25992,10 @@ __DEV__ &&
2598325992
!(function () {
2598425993
var internals = {
2598525994
bundleType: 1,
25986-
version: "19.1.0-native-fb-056073de-20250109",
25995+
version: "19.1.0-native-fb-0bf1f39e-20250110",
2598725996
rendererPackageName: "react-dom",
2598825997
currentDispatcherRef: ReactSharedInternals,
25989-
reconcilerVersion: "19.1.0-native-fb-056073de-20250109"
25998+
reconcilerVersion: "19.1.0-native-fb-0bf1f39e-20250110"
2599025999
};
2599126000
internals.overrideHookState = overrideHookState;
2599226001
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
@@ -26132,5 +26141,5 @@ __DEV__ &&
2613226141
listenToAllSupportedEvents(container);
2613326142
return new ReactDOMHydrationRoot(initialChildren);
2613426143
};
26135-
exports.version = "19.1.0-native-fb-056073de-20250109";
26144+
exports.version = "19.1.0-native-fb-0bf1f39e-20250110";
2613626145
})();

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/react-dom/cjs/ReactDOMClient-prod.js

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<6d0bb599e6dffc06990ad7b4086e34bb>>
10+
* @generated SignedSource<<6c245b9d2357f710e83bb65e2bca53dc>>
1111
*/
1212

1313
/*
@@ -8051,15 +8051,14 @@ function safelyAttachRef(current, nearestMountedAncestor) {
80518051
try {
80528052
var ref = current.ref;
80538053
if (null !== ref) {
8054-
var instance = current.stateNode;
80558054
switch (current.tag) {
80568055
case 26:
80578056
case 27:
80588057
case 5:
8059-
var instanceToUse = instance;
8058+
var instanceToUse = current.stateNode;
80608059
break;
80618060
default:
8062-
instanceToUse = instance;
8061+
instanceToUse = current.stateNode;
80638062
}
80648063
"function" === typeof ref
80658064
? (current.refCleanup = ref(instanceToUse))
@@ -12461,20 +12460,20 @@ function extractEvents$1(
1246112460
}
1246212461
}
1246312462
for (
12464-
var i$jscomp$inline_1526 = 0;
12465-
i$jscomp$inline_1526 < simpleEventPluginEvents.length;
12466-
i$jscomp$inline_1526++
12463+
var i$jscomp$inline_1525 = 0;
12464+
i$jscomp$inline_1525 < simpleEventPluginEvents.length;
12465+
i$jscomp$inline_1525++
1246712466
) {
12468-
var eventName$jscomp$inline_1527 =
12469-
simpleEventPluginEvents[i$jscomp$inline_1526],
12470-
domEventName$jscomp$inline_1528 =
12471-
eventName$jscomp$inline_1527.toLowerCase(),
12472-
capitalizedEvent$jscomp$inline_1529 =
12473-
eventName$jscomp$inline_1527[0].toUpperCase() +
12474-
eventName$jscomp$inline_1527.slice(1);
12467+
var eventName$jscomp$inline_1526 =
12468+
simpleEventPluginEvents[i$jscomp$inline_1525],
12469+
domEventName$jscomp$inline_1527 =
12470+
eventName$jscomp$inline_1526.toLowerCase(),
12471+
capitalizedEvent$jscomp$inline_1528 =
12472+
eventName$jscomp$inline_1526[0].toUpperCase() +
12473+
eventName$jscomp$inline_1526.slice(1);
1247512474
registerSimpleEvent(
12476-
domEventName$jscomp$inline_1528,
12477-
"on" + capitalizedEvent$jscomp$inline_1529
12475+
domEventName$jscomp$inline_1527,
12476+
"on" + capitalizedEvent$jscomp$inline_1528
1247812477
);
1247912478
}
1248012479
registerSimpleEvent(ANIMATION_END, "onAnimationEnd");
@@ -15959,16 +15958,16 @@ ReactDOMHydrationRoot.prototype.unstable_scheduleHydration = function (target) {
1595915958
0 === i && attemptExplicitHydrationTarget(target);
1596015959
}
1596115960
};
15962-
var isomorphicReactPackageVersion$jscomp$inline_1769 = React.version;
15961+
var isomorphicReactPackageVersion$jscomp$inline_1768 = React.version;
1596315962
if (
15964-
"19.1.0-native-fb-056073de-20250109" !==
15965-
isomorphicReactPackageVersion$jscomp$inline_1769
15963+
"19.1.0-native-fb-0bf1f39e-20250110" !==
15964+
isomorphicReactPackageVersion$jscomp$inline_1768
1596615965
)
1596715966
throw Error(
1596815967
formatProdErrorMessage(
1596915968
527,
15970-
isomorphicReactPackageVersion$jscomp$inline_1769,
15971-
"19.1.0-native-fb-056073de-20250109"
15969+
isomorphicReactPackageVersion$jscomp$inline_1768,
15970+
"19.1.0-native-fb-0bf1f39e-20250110"
1597215971
)
1597315972
);
1597415973
ReactDOMSharedInternals.findDOMNode = function (componentOrElement) {
@@ -15988,24 +15987,24 @@ ReactDOMSharedInternals.findDOMNode = function (componentOrElement) {
1598815987
null === componentOrElement ? null : componentOrElement.stateNode;
1598915988
return componentOrElement;
1599015989
};
15991-
var internals$jscomp$inline_2217 = {
15990+
var internals$jscomp$inline_2216 = {
1599215991
bundleType: 0,
15993-
version: "19.1.0-native-fb-056073de-20250109",
15992+
version: "19.1.0-native-fb-0bf1f39e-20250110",
1599415993
rendererPackageName: "react-dom",
1599515994
currentDispatcherRef: ReactSharedInternals,
15996-
reconcilerVersion: "19.1.0-native-fb-056073de-20250109"
15995+
reconcilerVersion: "19.1.0-native-fb-0bf1f39e-20250110"
1599715996
};
1599815997
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
15999-
var hook$jscomp$inline_2218 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
15998+
var hook$jscomp$inline_2217 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
1600015999
if (
16001-
!hook$jscomp$inline_2218.isDisabled &&
16002-
hook$jscomp$inline_2218.supportsFiber
16000+
!hook$jscomp$inline_2217.isDisabled &&
16001+
hook$jscomp$inline_2217.supportsFiber
1600316002
)
1600416003
try {
16005-
(rendererID = hook$jscomp$inline_2218.inject(
16006-
internals$jscomp$inline_2217
16004+
(rendererID = hook$jscomp$inline_2217.inject(
16005+
internals$jscomp$inline_2216
1600716006
)),
16008-
(injectedHook = hook$jscomp$inline_2218);
16007+
(injectedHook = hook$jscomp$inline_2217);
1600916008
} catch (err) {}
1601016009
}
1601116010
exports.createRoot = function (container, options) {
@@ -16099,4 +16098,4 @@ exports.hydrateRoot = function (container, initialChildren, options) {
1609916098
listenToAllSupportedEvents(container);
1610016099
return new ReactDOMHydrationRoot(initialChildren);
1610116100
};
16102-
exports.version = "19.1.0-native-fb-056073de-20250109";
16101+
exports.version = "19.1.0-native-fb-0bf1f39e-20250110";

compiled-rn/facebook-fbsource/xplat/js/RKJSModules/vendor/react/react-dom/cjs/ReactDOMClient-profiling.js

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @noflow
88
* @nolint
99
* @preventMunge
10-
* @generated SignedSource<<ac7a1701472a637b2c77e02e39d83f0d>>
10+
* @generated SignedSource<<5ed29f95ffe3e0550d2f61efaf661e98>>
1111
*/
1212

1313
/*
@@ -8343,15 +8343,14 @@ function safelyAttachRef(current, nearestMountedAncestor) {
83438343
try {
83448344
var ref = current.ref;
83458345
if (null !== ref) {
8346-
var instance = current.stateNode;
83478346
switch (current.tag) {
83488347
case 26:
83498348
case 27:
83508349
case 5:
8351-
var instanceToUse = instance;
8350+
var instanceToUse = current.stateNode;
83528351
break;
83538352
default:
8354-
instanceToUse = instance;
8353+
instanceToUse = current.stateNode;
83558354
}
83568355
if ("function" === typeof ref)
83578356
if (shouldProfile(current))
@@ -13102,20 +13101,20 @@ function extractEvents$1(
1310213101
}
1310313102
}
1310413103
for (
13105-
var i$jscomp$inline_1619 = 0;
13106-
i$jscomp$inline_1619 < simpleEventPluginEvents.length;
13107-
i$jscomp$inline_1619++
13104+
var i$jscomp$inline_1618 = 0;
13105+
i$jscomp$inline_1618 < simpleEventPluginEvents.length;
13106+
i$jscomp$inline_1618++
1310813107
) {
13109-
var eventName$jscomp$inline_1620 =
13110-
simpleEventPluginEvents[i$jscomp$inline_1619],
13111-
domEventName$jscomp$inline_1621 =
13112-
eventName$jscomp$inline_1620.toLowerCase(),
13113-
capitalizedEvent$jscomp$inline_1622 =
13114-
eventName$jscomp$inline_1620[0].toUpperCase() +
13115-
eventName$jscomp$inline_1620.slice(1);
13108+
var eventName$jscomp$inline_1619 =
13109+
simpleEventPluginEvents[i$jscomp$inline_1618],
13110+
domEventName$jscomp$inline_1620 =
13111+
eventName$jscomp$inline_1619.toLowerCase(),
13112+
capitalizedEvent$jscomp$inline_1621 =
13113+
eventName$jscomp$inline_1619[0].toUpperCase() +
13114+
eventName$jscomp$inline_1619.slice(1);
1311613115
registerSimpleEvent(
13117-
domEventName$jscomp$inline_1621,
13118-
"on" + capitalizedEvent$jscomp$inline_1622
13116+
domEventName$jscomp$inline_1620,
13117+
"on" + capitalizedEvent$jscomp$inline_1621
1311913118
);
1312013119
}
1312113120
registerSimpleEvent(ANIMATION_END, "onAnimationEnd");
@@ -16608,16 +16607,16 @@ ReactDOMHydrationRoot.prototype.unstable_scheduleHydration = function (target) {
1660816607
0 === i && attemptExplicitHydrationTarget(target);
1660916608
}
1661016609
};
16611-
var isomorphicReactPackageVersion$jscomp$inline_1864 = React.version;
16610+
var isomorphicReactPackageVersion$jscomp$inline_1863 = React.version;
1661216611
if (
16613-
"19.1.0-native-fb-056073de-20250109" !==
16614-
isomorphicReactPackageVersion$jscomp$inline_1864
16612+
"19.1.0-native-fb-0bf1f39e-20250110" !==
16613+
isomorphicReactPackageVersion$jscomp$inline_1863
1661516614
)
1661616615
throw Error(
1661716616
formatProdErrorMessage(
1661816617
527,
16619-
isomorphicReactPackageVersion$jscomp$inline_1864,
16620-
"19.1.0-native-fb-056073de-20250109"
16618+
isomorphicReactPackageVersion$jscomp$inline_1863,
16619+
"19.1.0-native-fb-0bf1f39e-20250110"
1662116620
)
1662216621
);
1662316622
ReactDOMSharedInternals.findDOMNode = function (componentOrElement) {
@@ -16637,12 +16636,12 @@ ReactDOMSharedInternals.findDOMNode = function (componentOrElement) {
1663716636
null === componentOrElement ? null : componentOrElement.stateNode;
1663816637
return componentOrElement;
1663916638
};
16640-
var internals$jscomp$inline_1871 = {
16639+
var internals$jscomp$inline_1870 = {
1664116640
bundleType: 0,
16642-
version: "19.1.0-native-fb-056073de-20250109",
16641+
version: "19.1.0-native-fb-0bf1f39e-20250110",
1664316642
rendererPackageName: "react-dom",
1664416643
currentDispatcherRef: ReactSharedInternals,
16645-
reconcilerVersion: "19.1.0-native-fb-056073de-20250109",
16644+
reconcilerVersion: "19.1.0-native-fb-0bf1f39e-20250110",
1664616645
getLaneLabelMap: function () {
1664716646
for (
1664816647
var map = new Map(), lane = 1, index$292 = 0;
@@ -16660,16 +16659,16 @@ var internals$jscomp$inline_1871 = {
1666016659
}
1666116660
};
1666216661
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
16663-
var hook$jscomp$inline_2277 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
16662+
var hook$jscomp$inline_2276 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
1666416663
if (
16665-
!hook$jscomp$inline_2277.isDisabled &&
16666-
hook$jscomp$inline_2277.supportsFiber
16664+
!hook$jscomp$inline_2276.isDisabled &&
16665+
hook$jscomp$inline_2276.supportsFiber
1666716666
)
1666816667
try {
16669-
(rendererID = hook$jscomp$inline_2277.inject(
16670-
internals$jscomp$inline_1871
16668+
(rendererID = hook$jscomp$inline_2276.inject(
16669+
internals$jscomp$inline_1870
1667116670
)),
16672-
(injectedHook = hook$jscomp$inline_2277);
16671+
(injectedHook = hook$jscomp$inline_2276);
1667316672
} catch (err) {}
1667416673
}
1667516674
exports.createRoot = function (container, options) {
@@ -16763,4 +16762,4 @@ exports.hydrateRoot = function (container, initialChildren, options) {
1676316762
listenToAllSupportedEvents(container);
1676416763
return new ReactDOMHydrationRoot(initialChildren);
1676516764
};
16766-
exports.version = "19.1.0-native-fb-056073de-20250109";
16765+
exports.version = "19.1.0-native-fb-0bf1f39e-20250110";

0 commit comments

Comments
 (0)