Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8af6728

Browse files
authored
Enable Suspense + rename Placeholder (facebook#13799)
* Enable Suspense * <unstable_Placeholder delayMs> => <unstable_Suspense maxDuration> * Update suspense fixture
1 parent f47a958 commit 8af6728

30 files changed

+326
-362
lines changed

fixtures/unstable-async/suspense/README.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,13 @@ No. The APIs being tested here are unstable and some of them have still not been
1414

1515
Clone the React repository.
1616

17-
First, open this file locally:
18-
19-
* `packages/shared/ReactFeatureFlags.js` (make sure you didn't open a similarly named file!)
20-
21-
Set [the `enableSuspense` flag](https://github.com/facebook/react/blob/d79238f1eeb6634ba7a3df23c3b2709b56cbb8b2/packages/shared/ReactFeatureFlags.js#L19) to `true` and save the file.
22-
23-
**After you've done that,** follow these steps:
17+
Follow these steps:
2418

2519
```shell
2620
# 1: Build react from source
2721
cd /path/to/react
2822
yarn
29-
yarn build dom-client,core,react-cache,schedule --type=NODE
23+
yarn build dom-client,core,react-cache,scheduler --type=NODE
3024

3125
# 2: Install fixture dependencies
3226
cd fixtures/unstable-async/suspense/

fixtures/unstable-async/suspense/src/components/App.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {Placeholder, PureComponent} from 'react';
1+
import React, {unstable_Suspense as Suspense, PureComponent} from 'react';
22
import {unstable_scheduleCallback} from 'scheduler';
33
import {
44
unstable_trace as trace,
@@ -76,21 +76,21 @@ export default class App extends PureComponent {
7676
}}>
7777
Return to list
7878
</button>
79-
<Placeholder delayMs={2000} fallback={<Spinner size="large" />}>
79+
<Suspense maxDuration={2000} fallback={<Spinner size="large" />}>
8080
<UserPageLoader id={id} />
81-
</Placeholder>
81+
</Suspense>
8282
</div>
8383
);
8484
}
8585

8686
renderList(loadingId) {
8787
return (
88-
<Placeholder delayMs={1500} fallback={<Spinner size="large" />}>
88+
<Suspense maxDuration={1500} fallback={<Spinner size="large" />}>
8989
<ContributorListPage
9090
loadingId={loadingId}
9191
onUserClick={this.handleUserClick}
9292
/>
93-
</Placeholder>
93+
</Suspense>
9494
);
9595
}
9696
}

fixtures/unstable-async/suspense/src/components/UserPage.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {Placeholder} from 'react';
1+
import React, {unstable_Suspense as Suspense} from 'react';
22
import {createResource} from 'react-cache';
33
import Spinner from './Spinner';
44
import {cache} from '../cache';
@@ -14,9 +14,9 @@ export default function UserPage({id}) {
1414
alignItems: 'start',
1515
}}>
1616
<UserDetails id={id} />
17-
<Placeholder delayMs={1000} fallback={<Spinner size="medium" />}>
17+
<Suspense maxDuration={1000} fallback={<Spinner size="medium" />}>
1818
<Repositories id={id} />
19-
</Placeholder>
19+
</Suspense>
2020
</div>
2121
);
2222
}
@@ -118,7 +118,7 @@ function Img({src, alt, ...rest}) {
118118

119119
function UserPicture({source}) {
120120
return (
121-
<Placeholder delayMs={1500} fallback={<img src={source} alt="poster" />}>
121+
<Suspense maxDuration={1500} fallback={<img src={source} alt="poster" />}>
122122
<Img
123123
src={source}
124124
alt="profile picture"
@@ -128,7 +128,7 @@ function UserPicture({source}) {
128128
borderRadius: '0.5rem',
129129
}}
130130
/>
131-
</Placeholder>
131+
</Suspense>
132132
);
133133
}
134134

packages/react-dom/src/__tests__/ReactDOMServerPlaceholders-test.internal.js renamed to packages/react-dom/src/__tests__/ReactDOMServerSuspense-test.internal.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ function initModules() {
2121
jest.resetModuleRegistry();
2222

2323
ReactFeatureFlags = require('shared/ReactFeatureFlags');
24-
ReactFeatureFlags.enableSuspense = true;
2524
ReactFeatureFlags.enableSuspenseServerRenderer = true;
2625

2726
React = require('react');
@@ -39,7 +38,7 @@ const {resetModules, serverRender} = ReactDOMServerIntegrationUtils(
3938
initModules,
4039
);
4140

42-
describe('ReactDOMServerPlaceholders', () => {
41+
describe('ReactDOMServerSuspense', () => {
4342
beforeEach(() => {
4443
resetModules();
4544
});
@@ -49,9 +48,9 @@ describe('ReactDOMServerPlaceholders', () => {
4948
throw new Promise(() => {});
5049
};
5150
const e = await serverRender(
52-
<React.Placeholder fallback={<div />}>
51+
<React.unstable_Suspense fallback={<div />}>
5352
<Suspended />
54-
</React.Placeholder>,
53+
</React.unstable_Suspense>,
5554
);
5655

5756
expect(e.tagName).toBe('DIV');

packages/react-dom/src/server/ReactPartialRenderer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
REACT_FRAGMENT_TYPE,
3434
REACT_STRICT_MODE_TYPE,
3535
REACT_CONCURRENT_MODE_TYPE,
36-
REACT_PLACEHOLDER_TYPE,
36+
REACT_SUSPENSE_TYPE,
3737
REACT_PORTAL_TYPE,
3838
REACT_PROFILER_TYPE,
3939
REACT_PROVIDER_TYPE,
@@ -916,7 +916,7 @@ class ReactDOMServerRenderer {
916916
this.stack.push(frame);
917917
return '';
918918
}
919-
case REACT_PLACEHOLDER_TYPE: {
919+
case REACT_SUSPENSE_TYPE: {
920920
if (enableSuspenseServerRenderer) {
921921
const nextChildren = toArray(
922922
// Always use the fallback when synchronously rendering to string.

packages/react-reconciler/src/ReactFiber.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
ContextProvider,
3434
ContextConsumer,
3535
Profiler,
36-
PlaceholderComponent,
36+
SuspenseComponent,
3737
FunctionComponentLazy,
3838
ClassComponentLazy,
3939
ForwardRefLazy,
@@ -58,7 +58,7 @@ import {
5858
REACT_PROVIDER_TYPE,
5959
REACT_CONTEXT_TYPE,
6060
REACT_CONCURRENT_MODE_TYPE,
61-
REACT_PLACEHOLDER_TYPE,
61+
REACT_SUSPENSE_TYPE,
6262
REACT_PURE_TYPE,
6363
} from 'shared/ReactSymbols';
6464

@@ -442,8 +442,8 @@ export function createFiberFromElement(
442442
break;
443443
case REACT_PROFILER_TYPE:
444444
return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
445-
case REACT_PLACEHOLDER_TYPE:
446-
fiberTag = PlaceholderComponent;
445+
case REACT_SUSPENSE_TYPE:
446+
fiberTag = SuspenseComponent;
447447
break;
448448
default: {
449449
if (typeof type === 'object' && type !== null) {

packages/react-reconciler/src/ReactFiberBeginWork.js

Lines changed: 58 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
ContextProvider,
3131
ContextConsumer,
3232
Profiler,
33-
PlaceholderComponent,
33+
SuspenseComponent,
3434
PureComponent,
3535
PureComponentLazy,
3636
} from 'shared/ReactWorkTags';
@@ -45,7 +45,6 @@ import {
4545
} from 'shared/ReactSideEffectTags';
4646
import ReactSharedInternals from 'shared/ReactSharedInternals';
4747
import {
48-
enableSuspense,
4948
debugRenderPhaseSideEffects,
5049
debugRenderPhaseSideEffectsForStrictMode,
5150
enableProfilerTimer,
@@ -935,77 +934,72 @@ function mountIndeterminateComponent(
935934
}
936935
}
937936

938-
function updatePlaceholderComponent(
937+
function updateSuspenseComponent(
939938
current,
940939
workInProgress,
941940
renderExpirationTime,
942941
) {
943-
if (enableSuspense) {
944-
const nextProps = workInProgress.pendingProps;
945-
946-
// Check if we already attempted to render the normal state. If we did,
947-
// and we timed out, render the placeholder state.
948-
const alreadyCaptured =
949-
(workInProgress.effectTag & DidCapture) === NoEffect;
950-
951-
let nextDidTimeout;
952-
if (current !== null && workInProgress.updateQueue !== null) {
953-
// We're outside strict mode. Something inside this Placeholder boundary
954-
// suspended during the last commit. Switch to the placholder.
955-
workInProgress.updateQueue = null;
956-
nextDidTimeout = true;
957-
} else {
958-
nextDidTimeout = !alreadyCaptured;
959-
}
942+
const nextProps = workInProgress.pendingProps;
960943

961-
if ((workInProgress.mode & StrictMode) !== NoEffect) {
962-
if (nextDidTimeout) {
963-
// If the timed-out view commits, schedule an update effect to record
964-
// the committed time.
965-
workInProgress.effectTag |= Update;
966-
} else {
967-
// The state node points to the time at which placeholder timed out.
968-
// We can clear it once we switch back to the normal children.
969-
workInProgress.stateNode = null;
970-
}
971-
}
944+
// Check if we already attempted to render the normal state. If we did,
945+
// and we timed out, render the placeholder state.
946+
const alreadyCaptured = (workInProgress.effectTag & DidCapture) === NoEffect;
972947

973-
// If the `children` prop is a function, treat it like a render prop.
974-
// TODO: This is temporary until we finalize a lower level API.
975-
const children = nextProps.children;
976-
let nextChildren;
977-
if (typeof children === 'function') {
978-
nextChildren = children(nextDidTimeout);
979-
} else {
980-
nextChildren = nextDidTimeout ? nextProps.fallback : children;
981-
}
948+
let nextDidTimeout;
949+
if (current !== null && workInProgress.updateQueue !== null) {
950+
// We're outside strict mode. Something inside this Placeholder boundary
951+
// suspended during the last commit. Switch to the placholder.
952+
workInProgress.updateQueue = null;
953+
nextDidTimeout = true;
954+
} else {
955+
nextDidTimeout = !alreadyCaptured;
956+
}
982957

983-
if (current !== null && nextDidTimeout !== workInProgress.memoizedState) {
984-
// We're about to switch from the placeholder children to the normal
985-
// children, or vice versa. These are two different conceptual sets that
986-
// happen to be stored in the same set. Call this special function to
987-
// force the new set not to match with the current set.
988-
// TODO: The proper way to model this is by storing each set separately.
989-
forceUnmountCurrentAndReconcile(
990-
current,
991-
workInProgress,
992-
nextChildren,
993-
renderExpirationTime,
994-
);
958+
if ((workInProgress.mode & StrictMode) !== NoEffect) {
959+
if (nextDidTimeout) {
960+
// If the timed-out view commits, schedule an update effect to record
961+
// the committed time.
962+
workInProgress.effectTag |= Update;
995963
} else {
996-
reconcileChildren(
997-
current,
998-
workInProgress,
999-
nextChildren,
1000-
renderExpirationTime,
1001-
);
964+
// The state node points to the time at which placeholder timed out.
965+
// We can clear it once we switch back to the normal children.
966+
workInProgress.stateNode = null;
1002967
}
1003-
workInProgress.memoizedProps = nextProps;
1004-
workInProgress.memoizedState = nextDidTimeout;
1005-
return workInProgress.child;
968+
}
969+
970+
// If the `children` prop is a function, treat it like a render prop.
971+
// TODO: This is temporary until we finalize a lower level API.
972+
const children = nextProps.children;
973+
let nextChildren;
974+
if (typeof children === 'function') {
975+
nextChildren = children(nextDidTimeout);
1006976
} else {
1007-
return null;
977+
nextChildren = nextDidTimeout ? nextProps.fallback : children;
978+
}
979+
980+
if (current !== null && nextDidTimeout !== workInProgress.memoizedState) {
981+
// We're about to switch from the placeholder children to the normal
982+
// children, or vice versa. These are two different conceptual sets that
983+
// happen to be stored in the same set. Call this special function to
984+
// force the new set not to match with the current set.
985+
// TODO: The proper way to model this is by storing each set separately.
986+
forceUnmountCurrentAndReconcile(
987+
current,
988+
workInProgress,
989+
nextChildren,
990+
renderExpirationTime,
991+
);
992+
} else {
993+
reconcileChildren(
994+
current,
995+
workInProgress,
996+
nextChildren,
997+
renderExpirationTime,
998+
);
1008999
}
1000+
workInProgress.memoizedProps = nextProps;
1001+
workInProgress.memoizedState = nextDidTimeout;
1002+
return workInProgress.child;
10091003
}
10101004

10111005
function updatePortalComponent(
@@ -1342,8 +1336,8 @@ function beginWork(
13421336
return updateHostComponent(current, workInProgress, renderExpirationTime);
13431337
case HostText:
13441338
return updateHostText(current, workInProgress);
1345-
case PlaceholderComponent:
1346-
return updatePlaceholderComponent(
1339+
case SuspenseComponent:
1340+
return updateSuspenseComponent(
13471341
current,
13481342
workInProgress,
13491343
renderExpirationTime,

packages/react-reconciler/src/ReactFiberCommitWork.js

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import type {CapturedValue, CapturedError} from './ReactCapturedValue';
2222
import {
2323
enableSchedulerTracing,
2424
enableProfilerTimer,
25-
enableSuspense,
2625
} from 'shared/ReactFeatureFlags';
2726
import {
2827
ClassComponent,
@@ -32,7 +31,7 @@ import {
3231
HostText,
3332
HostPortal,
3433
Profiler,
35-
PlaceholderComponent,
34+
SuspenseComponent,
3635
} from 'shared/ReactWorkTags';
3736
import {
3837
invokeGuardedCallback,
@@ -352,22 +351,20 @@ function commitLifeCycles(
352351
}
353352
return;
354353
}
355-
case PlaceholderComponent: {
356-
if (enableSuspense) {
357-
if ((finishedWork.mode & StrictMode) === NoEffect) {
358-
// In loose mode, a placeholder times out by scheduling a synchronous
359-
// update in the commit phase. Use `updateQueue` field to signal that
360-
// the Timeout needs to switch to the placeholder. We don't need an
361-
// entire queue. Any non-null value works.
362-
// $FlowFixMe - Intentionally using a value other than an UpdateQueue.
363-
finishedWork.updateQueue = emptyObject;
364-
scheduleWork(finishedWork, Sync);
365-
} else {
366-
// In strict mode, the Update effect is used to record the time at
367-
// which the placeholder timed out.
368-
const currentTime = requestCurrentTime();
369-
finishedWork.stateNode = {timedOutAt: currentTime};
370-
}
354+
case SuspenseComponent: {
355+
if ((finishedWork.mode & StrictMode) === NoEffect) {
356+
// In loose mode, a placeholder times out by scheduling a synchronous
357+
// update in the commit phase. Use `updateQueue` field to signal that
358+
// the Timeout needs to switch to the placeholder. We don't need an
359+
// entire queue. Any non-null value works.
360+
// $FlowFixMe - Intentionally using a value other than an UpdateQueue.
361+
finishedWork.updateQueue = emptyObject;
362+
scheduleWork(finishedWork, Sync);
363+
} else {
364+
// In strict mode, the Update effect is used to record the time at
365+
// which the placeholder timed out.
366+
const currentTime = requestCurrentTime();
367+
finishedWork.stateNode = {timedOutAt: currentTime};
371368
}
372369
return;
373370
}
@@ -863,7 +860,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
863860
case Profiler: {
864861
return;
865862
}
866-
case PlaceholderComponent: {
863+
case SuspenseComponent: {
867864
return;
868865
}
869866
default: {

0 commit comments

Comments
 (0)