@@ -1576,6 +1576,69 @@ function cancelAllViewTransitionAnimations(scope: Element) {
15761576// either cached the font or preloaded it earlier.
15771577const SUSPENSEY_FONT_TIMEOUT = 500 ;
15781578
1579+ function customizeViewTransitionError ( error : Object ) : mixed {
1580+ if ( typeof error === 'object' && error !== null ) {
1581+ switch ( error . name ) {
1582+ case 'TimeoutError ': {
1583+ // We assume that the only reason a Timeout can happen is because the Navigation
1584+ // promise. We expect any other work to either be fast or have a timeout (fonts).
1585+ if ( __DEV__ ) {
1586+ // eslint-disable-next-line react-internal/prod-error-codes
1587+ return new Error (
1588+ 'A ViewTransition timed out because a Navigation stalled. ' +
1589+ 'This can happen if a Navigation is blocked on React itself. ' +
1590+ "Such as if it's resolved inside useEffect. " +
1591+ 'This can be solved by moving the resolution to useLayoutEffect.' ,
1592+ { cause : error } ,
1593+ ) ;
1594+ }
1595+ break ;
1596+ }
1597+ case 'AbortError ': {
1598+ if ( __DEV__ ) {
1599+ // eslint-disable-next-line react-internal/prod-error-codes
1600+ return new Error (
1601+ 'A ViewTransition was aborted early. This might be because you have ' +
1602+ 'other View Transition libraries on the page and only one can run at ' +
1603+ "a time. To avoid this, use only React's built-in <ViewTransition> " +
1604+ 'to coordinate.' ,
1605+ { cause : error } ,
1606+ ) ;
1607+ }
1608+ break ;
1609+ }
1610+ case 'InvalidStateError ': {
1611+ if (
1612+ error . message ===
1613+ 'View transition was skipped because document visibility state is hidden.' ||
1614+ error . message ===
1615+ 'Skipping view transition because document visibility state has become hidden.' ||
1616+ error . message ===
1617+ 'Skipping view transition because viewport size changed.'
1618+ ) {
1619+ // Skip logging this. This is not considered an error.
1620+ return null ;
1621+ }
1622+ if ( __DEV__ ) {
1623+ if (
1624+ error . message === 'Transition was aborted because of invalid state'
1625+ ) {
1626+ // Chrome doesn't include the reason in the message but logs it in the console..
1627+ // Redirect the user to look there.
1628+ // eslint-disable-next-line react-internal/prod-error-codes
1629+ return new Error (
1630+ 'A ViewTransition could not start. See the console for more details.' ,
1631+ { cause : error } ,
1632+ ) ;
1633+ }
1634+ }
1635+ break ;
1636+ }
1637+ }
1638+ }
1639+ return error ;
1640+ }
1641+
15791642export function startViewTransition (
15801643 rootContainer : Container ,
15811644 transitionTypes : null | TransitionTypes ,
@@ -1584,6 +1647,7 @@ export function startViewTransition(
15841647 afterMutationCallback : ( ) = > void ,
15851648 spawnedWorkCallback : ( ) = > void ,
15861649 passiveCallback : ( ) = > mixed ,
1650+ errorCallback : mixed => void ,
15871651) : boolean {
15881652 const ownerDocument : Document =
15891653 rootContainer . nodeType === DOCUMENT_NODE
@@ -1641,24 +1705,19 @@ export function startViewTransition(
16411705 } ) ;
16421706 // $FlowFixMe[prop-missing]
16431707 ownerDocument . __reactViewTransition = transition ;
1644- if ( __DEV__ ) {
1645- transition . ready . then ( undefined , ( reason : mixed ) => {
1646- if (
1647- typeof reason === 'object' &&
1648- reason !== null &&
1649- reason . name === 'TimeoutError'
1650- ) {
1651- console . error (
1652- 'A ViewTransition timed out because a Navigation stalled. ' +
1653- 'This can happen if a Navigation is blocked on React itself. ' +
1654- "Such as if it's resolved inside useEffect. " +
1655- 'This can be solved by moving the resolution to useLayoutEffect.' ,
1656- ) ;
1708+ const handleError = ( error : mixed ) => {
1709+ try {
1710+ error = customizeViewTransitionError ( error ) ;
1711+ if ( error !== null ) {
1712+ errorCallback ( error ) ;
16571713 }
1658- } ) ;
1659- }
1660- transition . ready . then ( spawnedWorkCallback , spawnedWorkCallback ) ;
1661- transition . finished . then ( ( ) => {
1714+ } finally {
1715+ // Continue the reset of the work.
1716+ spawnedWorkCallback ( ) ;
1717+ }
1718+ } ;
1719+ transition . ready . then ( spawnedWorkCallback , handleError ) ;
1720+ transition . finished . finally ( ( ) => {
16621721 cancelAllViewTransitionAnimations ( ( ownerDocument . documentElement : any ) ) ;
16631722 // $FlowFixMe[prop-missing]
16641723 if ( ownerDocument . __reactViewTransition === transition ) {
@@ -1802,6 +1861,7 @@ export function startGestureTransition(
18021861 transitionTypes : null | TransitionTypes ,
18031862 mutationCallback : ( ) = > void ,
18041863 animateCallback : ( ) = > void ,
1864+ errorCallback : mixed => void ,
18051865) : null | RunningGestureTransition {
18061866 const ownerDocument : Document =
18071867 rootContainer . nodeType === DOCUMENT_NODE
@@ -1815,7 +1875,7 @@ export function startGestureTransition(
18151875 } ) ;
18161876 // $FlowFixMe[prop-missing]
18171877 ownerDocument . __reactViewTransition = transition ;
1818- const readyCallback = ( x : any ) => {
1878+ const readyCallback = ( ) => {
18191879 const documentElement : Element = ( ownerDocument . documentElement : any ) ;
18201880 // Loop through all View Transition Animations.
18211881 const animations = documentElement . getAnimations ( { subtree : true } ) ;
@@ -1935,8 +1995,19 @@ export function startGestureTransition(
19351995 navigator . userAgent . indexOf ( 'Chrome' ) !== - 1
19361996 ? ( ) => requestAnimationFrame ( readyCallback )
19371997 : readyCallback ;
1938- transition . ready . then ( readyForAnimations , readyCallback ) ;
1939- transition . finished . then ( ( ) => {
1998+ const handleError = ( error : mixed ) => {
1999+ try {
2000+ error = customizeViewTransitionError ( error ) ;
2001+ if ( error !== null ) {
2002+ errorCallback ( error ) ;
2003+ }
2004+ } finally {
2005+ // Continue the reset of the work.
2006+ readyCallback ( ) ;
2007+ }
2008+ } ;
2009+ transition . ready . then ( readyForAnimations , handleError ) ;
2010+ transition . finished . finally ( ( ) => {
19402011 cancelAllViewTransitionAnimations ( ( ownerDocument . documentElement : any ) ) ;
19412012 // $FlowFixMe[prop-missing]
19422013 if ( ownerDocument . __reactViewTransition === transition ) {
0 commit comments