Skip to content

Commit

Permalink
Move SuspenseList to experimental channel (#22765)
Browse files Browse the repository at this point in the history
There's more work to be done to implement this correctly on the server,
so we're going to wait to release it until an 18.x minor.
  • Loading branch information
acdlite committed Nov 15, 2021
1 parent 489b4bd commit 8356471
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 23 deletions.
5 changes: 4 additions & 1 deletion packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Expand Up @@ -42,7 +42,9 @@ describe('ReactDOMFizzServer', () => {
}
Stream = require('stream');
Suspense = React.Suspense;
SuspenseList = React.SuspenseList;
if (gate(flags => flags.enableSuspenseList)) {
SuspenseList = React.SuspenseList;
}

PropTypes = require('prop-types');

Expand Down Expand Up @@ -656,6 +658,7 @@ describe('ReactDOMFizzServer', () => {
expect(ref.current).toBe(b);
});

// @gate enableSuspenseList
// @gate experimental
it('shows inserted items before pending in a SuspenseList as fallbacks while hydrating', async () => {
const ref = React.createRef();
Expand Down
Expand Up @@ -84,7 +84,9 @@ describe('ReactDOMServerPartialHydration', () => {
ReactDOMServer = require('react-dom/server');
Scheduler = require('scheduler');
Suspense = React.Suspense;
SuspenseList = React.SuspenseList;
if (gate(flags => flags.enableSuspenseList)) {
SuspenseList = React.SuspenseList;
}

IdleEventPriority = require('react-reconciler/constants').IdleEventPriority;
});
Expand Down Expand Up @@ -1545,6 +1547,7 @@ describe('ReactDOMServerPartialHydration', () => {
expect(ref.current).toBe(span);
});

// @gate enableSuspenseList
it('shows inserted items in a SuspenseList before content is hydrated', async () => {
let suspend = false;
let resolve;
Expand Down Expand Up @@ -1630,6 +1633,7 @@ describe('ReactDOMServerPartialHydration', () => {
expect(ref.current).toBe(spanB);
});

// @gate enableSuspenseList
it('shows is able to hydrate boundaries even if others in a list are pending', async () => {
let suspend = false;
let resolve;
Expand Down Expand Up @@ -1704,7 +1708,7 @@ describe('ReactDOMServerPartialHydration', () => {
expect(container.textContent).toBe('ALoading B');
});

// @gate experimental || www
// @gate enableSuspenseList
it('clears server boundaries when SuspenseList runs out of time hydrating', async () => {
let suspend = false;
let resolve;
Expand Down Expand Up @@ -1807,6 +1811,7 @@ describe('ReactDOMServerPartialHydration', () => {
expect(ref.current).toBe(b);
});

// @gate enableSuspenseList
it('clears server boundaries when SuspenseList suspends last row hydrating', async () => {
let suspend = false;
let resolve;
Expand Down
Expand Up @@ -16,6 +16,7 @@ let ReactDOM;
let ReactDOMServer;
let ReactTestUtils;
let act;
let SuspenseList;

function initModules() {
// Reset warning cache.
Expand All @@ -26,6 +27,9 @@ function initModules() {
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');
act = require('jest-react').act;
if (gate(flags => flags.enableSuspenseList)) {
SuspenseList = React.SuspenseList;
}

// Make them available to the helpers.
return {
Expand Down Expand Up @@ -137,16 +141,17 @@ describe('ReactDOMServerSuspense', () => {
);
});

// @gate enableSuspenseList
it('server renders a SuspenseList component and its children', async () => {
const example = (
<React.SuspenseList>
<SuspenseList>
<React.Suspense fallback="Loading A">
<div>A</div>
</React.Suspense>
<React.Suspense fallback="Loading B">
<div>B</div>
</React.Suspense>
</React.SuspenseList>
</SuspenseList>
);
const element = await serverRender(example);
const parent = element.parentNode;
Expand Down
Expand Up @@ -24,7 +24,9 @@ beforeEach(() => {
act = require('jest-react').act;

Suspense = React.Suspense;
SuspenseList = React.SuspenseList;
if (gate(flags => flags.enableSuspenseList)) {
SuspenseList = React.SuspenseList;
}

getCacheForType = React.unstable_getCacheForType;

Expand Down Expand Up @@ -197,6 +199,7 @@ test('warns in DEV if return pointer is inconsistent', async () => {
});

// @gate enableCache
// @gate enableSuspenseList
test('regression (#20932): return pointer is correct before entering deleted tree', async () => {
// Based on a production bug. Designed to trigger a very specific
// implementation path.
Expand Down
12 changes: 9 additions & 3 deletions packages/react-is/src/__tests__/ReactIs-test.js
Expand Up @@ -12,6 +12,7 @@
let React;
let ReactDOM;
let ReactIs;
let SuspenseList;

describe('ReactIs', () => {
beforeEach(() => {
Expand All @@ -20,6 +21,10 @@ describe('ReactIs', () => {
React = require('react');
ReactDOM = require('react-dom');
ReactIs = require('react-is');

if (gate(flags => flags.enableSuspenseList)) {
SuspenseList = React.SuspenseList;
}
});

it('should return undefined for unknown/invalid types', () => {
Expand Down Expand Up @@ -186,10 +191,11 @@ describe('ReactIs', () => {
expect(ReactIs.isSuspense(<div />)).toBe(false);
});

// @gate enableSuspenseList
it('should identify suspense list', () => {
expect(ReactIs.isValidElementType(React.SuspenseList)).toBe(true);
expect(ReactIs.typeOf(<React.SuspenseList />)).toBe(ReactIs.SuspenseList);
expect(ReactIs.isSuspenseList(<React.SuspenseList />)).toBe(true);
expect(ReactIs.isValidElementType(SuspenseList)).toBe(true);
expect(ReactIs.typeOf(<SuspenseList />)).toBe(ReactIs.SuspenseList);
expect(ReactIs.isSuspenseList(<SuspenseList />)).toBe(true);
expect(ReactIs.isSuspenseList({type: ReactIs.SuspenseList})).toBe(false);
expect(ReactIs.isSuspenseList('React.SuspenseList')).toBe(false);
expect(ReactIs.isSuspenseList(<div />)).toBe(false);
Expand Down
Expand Up @@ -21,7 +21,9 @@ describe('ReactLazyContextPropagation', () => {
useState = React.useState;
useContext = React.useContext;
Suspense = React.Suspense;
SuspenseList = React.SuspenseList;
if (gate(flags => flags.enableSuspenseList)) {
SuspenseList = React.SuspenseList;
}

getCacheForType = React.unstable_getCacheForType;

Expand Down Expand Up @@ -651,6 +653,7 @@ describe('ReactLazyContextPropagation', () => {
expect(root).toMatchRenderedOutput('BBB');
});

// @gate enableSuspenseList
test('contexts are propagated through SuspenseList', async () => {
// This kinda tests an implementation detail. SuspenseList has an early
// bailout that doesn't use `bailoutOnAlreadyFinishedWork`. It probably
Expand Down
Expand Up @@ -34,6 +34,7 @@ let forwardRef;
let memo;
let act;
let ContinuousEventPriority;
let SuspenseList;

describe('ReactHooksWithNoopRenderer', () => {
beforeEach(() => {
Expand All @@ -60,6 +61,9 @@ describe('ReactHooksWithNoopRenderer', () => {
Suspense = React.Suspense;
ContinuousEventPriority = require('react-reconciler/constants')
.ContinuousEventPriority;
if (gate(flags => flags.enableSuspenseList)) {
SuspenseList = React.SuspenseList;
}

textCache = new Map();

Expand Down Expand Up @@ -4291,9 +4295,8 @@ describe('ReactHooksWithNoopRenderer', () => {
]);
});

// @gate enableSuspenseList
it('regression: SuspenseList causes unmounts to be dropped on deletion', async () => {
const SuspenseList = React.SuspenseList;

function Row({label}) {
useEffect(() => {
Scheduler.unstable_yieldValue('Mount ' + label);
Expand Down

0 comments on commit 8356471

Please sign in to comment.