@@ -1576,6 +1576,69 @@ function cancelAllViewTransitionAnimations(scope: Element) {
1576
1576
// either cached the font or preloaded it earlier.
1577
1577
const SUSPENSEY_FONT_TIMEOUT = 500 ;
1578
1578
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
+
1579
1642
export function startViewTransition (
1580
1643
rootContainer : Container ,
1581
1644
transitionTypes : null | TransitionTypes ,
@@ -1584,6 +1647,7 @@ export function startViewTransition(
1584
1647
afterMutationCallback : ( ) = > void ,
1585
1648
spawnedWorkCallback : ( ) = > void ,
1586
1649
passiveCallback : ( ) = > mixed ,
1650
+ errorCallback : mixed => void ,
1587
1651
) : boolean {
1588
1652
const ownerDocument : Document =
1589
1653
rootContainer . nodeType === DOCUMENT_NODE
@@ -1641,24 +1705,19 @@ export function startViewTransition(
1641
1705
} ) ;
1642
1706
// $FlowFixMe[prop-missing]
1643
1707
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 ) ;
1657
1713
}
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 ( ( ) => {
1662
1721
cancelAllViewTransitionAnimations ( ( ownerDocument . documentElement : any ) ) ;
1663
1722
// $FlowFixMe[prop-missing]
1664
1723
if ( ownerDocument . __reactViewTransition === transition ) {
@@ -1802,6 +1861,7 @@ export function startGestureTransition(
1802
1861
transitionTypes : null | TransitionTypes ,
1803
1862
mutationCallback : ( ) = > void ,
1804
1863
animateCallback : ( ) = > void ,
1864
+ errorCallback : mixed => void ,
1805
1865
) : null | RunningGestureTransition {
1806
1866
const ownerDocument : Document =
1807
1867
rootContainer . nodeType === DOCUMENT_NODE
@@ -1815,7 +1875,7 @@ export function startGestureTransition(
1815
1875
} ) ;
1816
1876
// $FlowFixMe[prop-missing]
1817
1877
ownerDocument . __reactViewTransition = transition ;
1818
- const readyCallback = ( x : any ) => {
1878
+ const readyCallback = ( ) => {
1819
1879
const documentElement : Element = ( ownerDocument . documentElement : any ) ;
1820
1880
// Loop through all View Transition Animations.
1821
1881
const animations = documentElement . getAnimations ( { subtree : true } ) ;
@@ -1935,8 +1995,19 @@ export function startGestureTransition(
1935
1995
navigator . userAgent . indexOf ( 'Chrome' ) !== - 1
1936
1996
? ( ) => requestAnimationFrame ( readyCallback )
1937
1997
: 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 ( ( ) => {
1940
2011
cancelAllViewTransitionAnimations ( ( ownerDocument . documentElement : any ) ) ;
1941
2012
// $FlowFixMe[prop-missing]
1942
2013
if ( ownerDocument . __reactViewTransition === transition ) {
0 commit comments