@@ -1701,4 +1701,282 @@ describe('ReactSuspenseWithNoopRenderer', () => {
17011701
17021702 expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'Loading A...' ) ] ) ;
17031703 } ) ;
1704+
1705+ describe ( 'delays transitions when there a suspense config is supplied' , ( ) => {
1706+ const SUSPENSE_CONFIG = {
1707+ timeoutMs : 2000 ,
1708+ } ;
1709+
1710+ it ( 'top level render' , async ( ) => {
1711+ function App ( { page} ) {
1712+ return (
1713+ < Suspense fallback = { < Text text = "Loading..." /> } >
1714+ < AsyncText text = { page } ms = { 5000 } />
1715+ </ Suspense >
1716+ ) ;
1717+ }
1718+
1719+ // Initial render.
1720+ React . unstable_withSuspenseConfig (
1721+ ( ) => ReactNoop . render ( < App page = "A" /> ) ,
1722+ SUSPENSE_CONFIG ,
1723+ ) ;
1724+
1725+ expect ( Scheduler ) . toFlushAndYield ( [ 'Suspend! [A]' , 'Loading...' ] ) ;
1726+ // Only a short time is needed to unsuspend the initial loading state.
1727+ Scheduler . advanceTime ( 400 ) ;
1728+ await advanceTimers ( 400 ) ;
1729+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'Loading...' ) ] ) ;
1730+
1731+ // Later we load the data.
1732+ Scheduler . advanceTime ( 5000 ) ;
1733+ await advanceTimers ( 5000 ) ;
1734+ expect ( Scheduler ) . toHaveYielded ( [ 'Promise resolved [A]' ] ) ;
1735+ expect ( Scheduler ) . toFlushAndYield ( [ 'A' ] ) ;
1736+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'A' ) ] ) ;
1737+
1738+ // Start transition.
1739+ React . unstable_withSuspenseConfig (
1740+ ( ) => ReactNoop . render ( < App page = "B" /> ) ,
1741+ SUSPENSE_CONFIG ,
1742+ ) ;
1743+
1744+ expect ( Scheduler ) . toFlushAndYield ( [ 'Suspend! [B]' , 'Loading...' ] ) ;
1745+ Scheduler . advanceTime ( 1000 ) ;
1746+ await advanceTimers ( 1000 ) ;
1747+ // Even after a second, we have still not yet flushed the loading state.
1748+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'A' ) ] ) ;
1749+ Scheduler . advanceTime ( 1100 ) ;
1750+ await advanceTimers ( 1100 ) ;
1751+ // After the timeout, we do show the loading state.
1752+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [
1753+ hiddenSpan ( 'A' ) ,
1754+ span ( 'Loading...' ) ,
1755+ ] ) ;
1756+ // Later we load the data.
1757+ Scheduler . advanceTime ( 3000 ) ;
1758+ await advanceTimers ( 3000 ) ;
1759+ expect ( Scheduler ) . toHaveYielded ( [ 'Promise resolved [B]' ] ) ;
1760+ expect ( Scheduler ) . toFlushAndYield ( [ 'B' ] ) ;
1761+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'B' ) ] ) ;
1762+ } ) ;
1763+
1764+ it ( 'hooks' , async ( ) => {
1765+ let transitionToPage ;
1766+ function App ( ) {
1767+ let [ page , setPage ] = React . useState ( 'none' ) ;
1768+ transitionToPage = setPage ;
1769+ if ( page === 'none' ) {
1770+ return null ;
1771+ }
1772+ return (
1773+ < Suspense fallback = { < Text text = "Loading..." /> } >
1774+ < AsyncText text = { page } ms = { 5000 } />
1775+ </ Suspense >
1776+ ) ;
1777+ }
1778+
1779+ ReactNoop . render ( < App /> ) ;
1780+ expect ( Scheduler ) . toFlushAndYield ( [ ] ) ;
1781+
1782+ // Initial render.
1783+ await ReactNoop . act ( async ( ) => {
1784+ React . unstable_withSuspenseConfig (
1785+ ( ) => transitionToPage ( 'A' ) ,
1786+ SUSPENSE_CONFIG ,
1787+ ) ;
1788+
1789+ expect ( Scheduler ) . toFlushAndYield ( [ 'Suspend! [A]' , 'Loading...' ] ) ;
1790+ // Only a short time is needed to unsuspend the initial loading state.
1791+ Scheduler . advanceTime ( 400 ) ;
1792+ await advanceTimers ( 400 ) ;
1793+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'Loading...' ) ] ) ;
1794+ } ) ;
1795+
1796+ // Later we load the data.
1797+ Scheduler . advanceTime ( 5000 ) ;
1798+ await advanceTimers ( 5000 ) ;
1799+ expect ( Scheduler ) . toHaveYielded ( [ 'Promise resolved [A]' ] ) ;
1800+ expect ( Scheduler ) . toFlushAndYield ( [ 'A' ] ) ;
1801+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'A' ) ] ) ;
1802+
1803+ // Start transition.
1804+ await ReactNoop . act ( async ( ) => {
1805+ React . unstable_withSuspenseConfig (
1806+ ( ) => transitionToPage ( 'B' ) ,
1807+ SUSPENSE_CONFIG ,
1808+ ) ;
1809+
1810+ expect ( Scheduler ) . toFlushAndYield ( [ 'Suspend! [B]' , 'Loading...' ] ) ;
1811+ Scheduler . advanceTime ( 1000 ) ;
1812+ await advanceTimers ( 1000 ) ;
1813+ // Even after a second, we have still not yet flushed the loading state.
1814+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'A' ) ] ) ;
1815+ Scheduler . advanceTime ( 1100 ) ;
1816+ await advanceTimers ( 1100 ) ;
1817+ // After the timeout, we do show the loading state.
1818+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [
1819+ hiddenSpan ( 'A' ) ,
1820+ span ( 'Loading...' ) ,
1821+ ] ) ;
1822+ } ) ;
1823+ // Later we load the data.
1824+ Scheduler . advanceTime ( 3000 ) ;
1825+ await advanceTimers ( 3000 ) ;
1826+ expect ( Scheduler ) . toHaveYielded ( [ 'Promise resolved [B]' ] ) ;
1827+ expect ( Scheduler ) . toFlushAndYield ( [ 'B' ] ) ;
1828+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'B' ) ] ) ;
1829+ } ) ;
1830+
1831+ it ( 'classes' , async ( ) => {
1832+ let transitionToPage ;
1833+ class App extends React . Component {
1834+ state = { page : 'none' } ;
1835+ render ( ) {
1836+ transitionToPage = page => this . setState ( { page} ) ;
1837+ let page = this . state . page ;
1838+ if ( page === 'none' ) {
1839+ return null ;
1840+ }
1841+ return (
1842+ < Suspense fallback = { < Text text = "Loading..." /> } >
1843+ < AsyncText text = { page } ms = { 5000 } />
1844+ </ Suspense >
1845+ ) ;
1846+ }
1847+ }
1848+
1849+ ReactNoop . render ( < App /> ) ;
1850+ expect ( Scheduler ) . toFlushAndYield ( [ ] ) ;
1851+
1852+ // Initial render.
1853+ await ReactNoop . act ( async ( ) => {
1854+ React . unstable_withSuspenseConfig (
1855+ ( ) => transitionToPage ( 'A' ) ,
1856+ SUSPENSE_CONFIG ,
1857+ ) ;
1858+
1859+ expect ( Scheduler ) . toFlushAndYield ( [ 'Suspend! [A]' , 'Loading...' ] ) ;
1860+ // Only a short time is needed to unsuspend the initial loading state.
1861+ Scheduler . advanceTime ( 400 ) ;
1862+ await advanceTimers ( 400 ) ;
1863+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'Loading...' ) ] ) ;
1864+ } ) ;
1865+
1866+ // Later we load the data.
1867+ Scheduler . advanceTime ( 5000 ) ;
1868+ await advanceTimers ( 5000 ) ;
1869+ expect ( Scheduler ) . toHaveYielded ( [ 'Promise resolved [A]' ] ) ;
1870+ expect ( Scheduler ) . toFlushAndYield ( [ 'A' ] ) ;
1871+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'A' ) ] ) ;
1872+
1873+ // Start transition.
1874+ await ReactNoop . act ( async ( ) => {
1875+ React . unstable_withSuspenseConfig (
1876+ ( ) => transitionToPage ( 'B' ) ,
1877+ SUSPENSE_CONFIG ,
1878+ ) ;
1879+
1880+ expect ( Scheduler ) . toFlushAndYield ( [ 'Suspend! [B]' , 'Loading...' ] ) ;
1881+ Scheduler . advanceTime ( 1000 ) ;
1882+ await advanceTimers ( 1000 ) ;
1883+ // Even after a second, we have still not yet flushed the loading state.
1884+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'A' ) ] ) ;
1885+ Scheduler . advanceTime ( 1100 ) ;
1886+ await advanceTimers ( 1100 ) ;
1887+ // After the timeout, we do show the loading state.
1888+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [
1889+ hiddenSpan ( 'A' ) ,
1890+ span ( 'Loading...' ) ,
1891+ ] ) ;
1892+ } ) ;
1893+ // Later we load the data.
1894+ Scheduler . advanceTime ( 3000 ) ;
1895+ await advanceTimers ( 3000 ) ;
1896+ expect ( Scheduler ) . toHaveYielded ( [ 'Promise resolved [B]' ] ) ;
1897+ expect ( Scheduler ) . toFlushAndYield ( [ 'B' ] ) ;
1898+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'B' ) ] ) ;
1899+ } ) ;
1900+ } ) ;
1901+
1902+ it ( 'disables suspense config when nothing is passed to withSuspenseConfig' , async ( ) => {
1903+ function App ( { page} ) {
1904+ return (
1905+ < Fragment >
1906+ < Suspense fallback = { < Text text = "Loading..." /> } >
1907+ < AsyncText text = { page } ms = { 2000 } />
1908+ </ Suspense >
1909+ </ Fragment >
1910+ ) ;
1911+ }
1912+
1913+ // Initial render.
1914+ ReactNoop . render ( < App page = "A" /> ) ;
1915+ expect ( Scheduler ) . toFlushAndYield ( [ 'Suspend! [A]' , 'Loading...' ] ) ;
1916+ Scheduler . advanceTime ( 2000 ) ;
1917+ await advanceTimers ( 2000 ) ;
1918+ expect ( Scheduler ) . toHaveYielded ( [ 'Promise resolved [A]' ] ) ;
1919+ expect ( Scheduler ) . toFlushAndYield ( [ 'A' ] ) ;
1920+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'A' ) ] ) ;
1921+
1922+ // Start transition.
1923+ React . unstable_withSuspenseConfig (
1924+ ( ) => {
1925+ // When we schedule an inner transition without a suspense config
1926+ // so it should only suspend for a short time.
1927+ React . unstable_withSuspenseConfig ( ( ) =>
1928+ ReactNoop . render ( < App page = "B" /> ) ,
1929+ ) ;
1930+ } ,
1931+ { timeoutMs : 2000 } ,
1932+ ) ;
1933+
1934+ expect ( Scheduler ) . toFlushAndYield ( [ 'Suspend! [B]' , 'Loading...' ] ) ;
1935+ // Suspended
1936+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'A' ) ] ) ;
1937+ Scheduler . advanceTime ( 500 ) ;
1938+ await advanceTimers ( 500 ) ;
1939+ // Committed loading state.
1940+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [
1941+ hiddenSpan ( 'A' ) ,
1942+ span ( 'Loading...' ) ,
1943+ ] ) ;
1944+
1945+ Scheduler . advanceTime ( 2000 ) ;
1946+ await advanceTimers ( 2000 ) ;
1947+ expect ( Scheduler ) . toHaveYielded ( [ 'Promise resolved [B]' ] ) ;
1948+ expect ( Scheduler ) . toFlushAndYield ( [ 'B' ] ) ;
1949+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'B' ) ] ) ;
1950+
1951+ React . unstable_withSuspenseConfig (
1952+ ( ) => {
1953+ // First we schedule an inner unrelated update.
1954+ React . unstable_withSuspenseConfig ( ( ) =>
1955+ ReactNoop . render ( < App page = "B" unrelated = { true } /> ) ,
1956+ ) ;
1957+ // Then we schedule another transition to a slow page,
1958+ // but at this scope we should suspend for longer.
1959+ Scheduler . unstable_next ( ( ) => ReactNoop . render ( < App page = "C" /> ) ) ;
1960+ } ,
1961+ { timeoutMs : 2000 } ,
1962+ ) ;
1963+ expect ( Scheduler ) . toFlushAndYield ( [
1964+ 'Suspend! [C]' ,
1965+ 'Loading...' ,
1966+ 'Suspend! [C]' ,
1967+ 'Loading...' ,
1968+ ] ) ;
1969+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'B' ) ] ) ;
1970+ Scheduler . advanceTime ( 1200 ) ;
1971+ await advanceTimers ( 1200 ) ;
1972+ // Even after a second, we have still not yet flushed the loading state.
1973+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'B' ) ] ) ;
1974+ Scheduler . advanceTime ( 1200 ) ;
1975+ await advanceTimers ( 1200 ) ;
1976+ // After the two second timeout we show the loading state.
1977+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [
1978+ hiddenSpan ( 'B' ) ,
1979+ span ( 'Loading...' ) ,
1980+ ] ) ;
1981+ } ) ;
17041982} ) ;
0 commit comments