@samitouri / QOS-React / commits / 88d7ed8bfb

React.Timeout -> React.Placeholder (#13105)

Changed the API to match what we've been using in our latest discussions. Our tentative plans are for <Placeholder> to automatically hide the timed-out children, instead of removing them, so their state is not lost. This part is not yet implemented. We'll likely have a lower level API that does not include the hiding behavior. This is also not yet implemented.

Andrew Clark committed Jul 3, 2018 at 19:47 UTC 88d7ed8bfbccd860c3e309da39d356d0a3127aa7
11 files changed +135 -127
packages/react-reconciler/src/ReactFiber.js
+4 -4
@@ -31,7 +31,7 @@ import {
31 ContextProvider,
32 ContextConsumer,
33 Profiler,
34 - TimeoutComponent,
34 + PlaceholderComponent,
35 } from 'shared/ReactTypeOfWork';
36 import getComponentName from 'shared/getComponentName';
37
@@ -46,7 +46,7 @@ import {
46 REACT_PROVIDER_TYPE,
47 REACT_CONTEXT_TYPE,
48 REACT_ASYNC_MODE_TYPE,
49 - REACT_TIMEOUT_TYPE,
49 + REACT_PLACEHOLDER_TYPE,
50 } from 'shared/ReactSymbols';
51
52 let hasBadMapPolyfill;
@@ -397,8 +397,8 @@ export function createFiberFromElement(
397 break;
398 case REACT_PROFILER_TYPE:
399 return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
400 - case REACT_TIMEOUT_TYPE:
401 - fiberTag = TimeoutComponent;
400 + case REACT_PLACEHOLDER_TYPE:
401 + fiberTag = PlaceholderComponent;
402 break;
403 default:
404 fiberTag = getFiberTagFromObjectType(type, owner);
packages/react-reconciler/src/ReactFiberBeginWork.js
+19 -7
@@ -27,7 +27,7 @@ import {
27 ContextProvider,
28 ContextConsumer,
29 Profiler,
30 - TimeoutComponent,
30 + PlaceholderComponent,
31 } from 'shared/ReactTypeOfWork';
32 import {
33 NoEffect,
@@ -703,7 +703,11 @@ function mountIndeterminateComponent(
703 }
704 }
705
706 -function updateTimeoutComponent(current, workInProgress, renderExpirationTime) {
706 +function updatePlaceholderComponent(
707 + current,
708 + workInProgress,
709 + renderExpirationTime,
710 +) {
711 if (enableSuspense) {
712 const nextProps = workInProgress.pendingProps;
713 const prevProps = workInProgress.memoizedProps;
@@ -717,7 +721,7 @@ function updateTimeoutComponent(current, workInProgress, renderExpirationTime) {
721
722 let nextDidTimeout;
723 if (current !== null && workInProgress.updateQueue !== null) {
720 - // We're outside strict mode. Something inside this Timeout boundary
724 + // We're outside strict mode. Something inside this Placeholder boundary
725 // suspended during the last commit. Switch to the placholder.
726 workInProgress.updateQueue = null;
727 nextDidTimeout = true;
@@ -756,8 +760,16 @@ function updateTimeoutComponent(current, workInProgress, renderExpirationTime) {
760 }
761 }
762
759 - const render = nextProps.children;
760 - const nextChildren = render(nextDidTimeout);
763 + // If the `children` prop is a function, treat it like a render prop.
764 + // TODO: This is temporary until we finalize a lower level API.
765 + const children = nextProps.children;
766 + let nextChildren;
767 + if (typeof children === 'function') {
768 + nextChildren = children(nextDidTimeout);
769 + } else {
770 + nextChildren = nextDidTimeout ? nextProps.fallback : children;
771 + }
772 +
773 workInProgress.memoizedProps = nextProps;
774 workInProgress.memoizedState = nextDidTimeout;
775 reconcileChildren(current, workInProgress, nextChildren);
@@ -1197,8 +1209,8 @@ function beginWork(
1209 return updateHostComponent(current, workInProgress, renderExpirationTime);
1210 case HostText:
1211 return updateHostText(current, workInProgress);
1200 - case TimeoutComponent:
1201 - return updateTimeoutComponent(
1212 + case PlaceholderComponent:
1213 + return updatePlaceholderComponent(
1214 current,
1215 workInProgress,
1216 renderExpirationTime,
packages/react-reconciler/src/ReactFiberCommitWork.js
+3 -3
@@ -28,7 +28,7 @@ import {
28 HostText,
29 HostPortal,
30 Profiler,
31 - TimeoutComponent,
31 + PlaceholderComponent,
32 } from 'shared/ReactTypeOfWork';
33 import ReactErrorUtils from 'shared/ReactErrorUtils';
34 import {
@@ -322,7 +322,7 @@ function commitLifeCycles(
322 // We have no life-cycles associated with Profiler.
323 return;
324 }
325 - case TimeoutComponent: {
325 + case PlaceholderComponent: {
326 if (enableSuspense) {
327 if ((finishedWork.mode & StrictMode) === NoEffect) {
328 // In loose mode, a placeholder times out by scheduling a synchronous
@@ -837,7 +837,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
837 }
838 return;
839 }
840 - case TimeoutComponent: {
840 + case PlaceholderComponent: {
841 return;
842 }
843 default: {
packages/react-reconciler/src/ReactFiberCompleteWork.js
+2 -2
@@ -35,7 +35,7 @@ import {
35 Fragment,
36 Mode,
37 Profiler,
38 - TimeoutComponent,
38 + PlaceholderComponent,
39 } from 'shared/ReactTypeOfWork';
40 import {Placement, Ref, Update} from 'shared/ReactTypeOfSideEffect';
41 import {ProfileMode} from './ReactTypeOfMode';
@@ -490,7 +490,7 @@ function completeWork(
490 }
491 case ForwardRef:
492 return null;
493 - case TimeoutComponent:
493 + case PlaceholderComponent:
494 return null;
495 case Fragment:
496 return null;
packages/react-reconciler/src/ReactFiberUnwindWork.js
+26 -25
@@ -22,7 +22,7 @@ import {
22 HostComponent,
23 HostPortal,
24 ContextProvider,
25 - TimeoutComponent,
25 + PlaceholderComponent,
26 } from 'shared/ReactTypeOfWork';
27 import {
28 DidCapture,
@@ -157,16 +157,16 @@ function throwException(
157 // This is a thenable.
158 const thenable: Thenable = (value: any);
159
160 - // Find the earliest timeout of all the timeouts in the ancestor path. We
161 - // could avoid this traversal by storing the timeouts on the stack, but we
162 - // choose not to because we only hit this path if we're IO-bound (i.e. if
163 - // something suspends). Whereas the stack is used even in the non-IO-
164 - // bound case.
160 + // Find the earliest timeout threshold of all the placeholders in the
161 + // ancestor path. We could avoid this traversal by storing the thresholds on
162 + // the stack, but we choose not to because we only hit this path if we're
163 + // IO-bound (i.e. if something suspends). Whereas the stack is used even in
164 + // the non-IO- bound case.
165 let workInProgress = returnFiber;
166 let earliestTimeoutMs = -1;
167 let startTimeMs = -1;
168 do {
169 - if (workInProgress.tag === TimeoutComponent) {
169 + if (workInProgress.tag === PlaceholderComponent) {
170 const current = workInProgress.alternate;
171 if (
172 current !== null &&
@@ -184,7 +184,7 @@ function throwException(
184 // Do not search any further.
185 break;
186 }
187 - let timeoutPropMs = workInProgress.pendingProps.ms;
187 + let timeoutPropMs = workInProgress.pendingProps.delayMs;
188 if (typeof timeoutPropMs === 'number') {
189 if (timeoutPropMs <= 0) {
190 earliestTimeoutMs = 0;
@@ -199,15 +199,15 @@ function throwException(
199 workInProgress = workInProgress.return;
200 } while (workInProgress !== null);
201
202 - // Schedule the nearest Timeout to re-render using a placeholder.
202 + // Schedule the nearest Placeholder to re-render the timed out view.
203 workInProgress = returnFiber;
204 do {
205 - if (workInProgress.tag === TimeoutComponent) {
205 + if (workInProgress.tag === PlaceholderComponent) {
206 const didTimeout = workInProgress.memoizedState;
207 if (!didTimeout) {
208 - // Found the nearest Timeout.
208 + // Found the nearest boundary.
209
210 - // If the Timeout is not in async mode, we should not suspend, and
210 + // If the boundary is not in async mode, we should not suspend, and
211 // likewise, when the promise resolves, we should ping synchronously.
212 const pingTime =
213 (workInProgress.mode & AsyncMode) === NoEffect
@@ -223,13 +223,13 @@ function throwException(
223 );
224 thenable.then(onResolveOrReject, onResolveOrReject);
225
226 - // If the Timeout component is outside of strict mode, we should *not*
227 - // suspend the commit. Pretend as if the suspended component rendered
228 - // null and keep rendering. In the commit phase, we'll schedule a
229 - // subsequent synchronous update to re-render the Timeout.
226 + // If the boundary is outside of strict mode, we should *not* suspend
227 + // the commit. Pretend as if the suspended component rendered null and
228 + // keep rendering. In the commit phase, we'll schedule a subsequent
229 + // synchronous update to re-render the Placeholder.
230 //
231 // Note: It doesn't matter whether the component that suspended was
232 - // inside a strict mode tree. If the Timeout is outside of it, we
232 + // inside a strict mode tree. If the Placeholder is outside of it, we
233 // should *not* suspend the commit.
234 if ((workInProgress.mode & StrictMode) === NoEffect) {
235 workInProgress.effectTag |= UpdateEffect;
@@ -253,7 +253,7 @@ function throwException(
253 return;
254 }
255
256 - // Confirmed that the Timeout is in a strict mode tree. Continue with
256 + // Confirmed that the bounary is in a strict mode tree. Continue with
257 // the normal suspend path.
258
259 let absoluteTimeoutMs;
@@ -285,20 +285,21 @@ function throwException(
285 absoluteTimeoutMs = startTimeMs + earliestTimeoutMs;
286 }
287
288 - // Mark the earliest timeout in the suspended fiber's ancestor path. After
289 - // completing the root, we'll take the largest of all the suspended
290 - // fiber's timeouts and use it to compute a timeout for the whole tree.
288 + // Mark the earliest timeout in the suspended fiber's ancestor path.
289 + // After completing the root, we'll take the largest of all the
290 + // suspended fiber's timeouts and use it to compute a timeout for the
291 + // whole tree.
292 renderDidSuspend(root, absoluteTimeoutMs, renderExpirationTime);
293
294 workInProgress.effectTag |= ShouldCapture;
295 return;
296 }
296 - // Already captured during this render. Continue to the next
297 - // Timeout ancestor.
297 + // This boundary already captured during this render. Continue to the
298 + // next boundary.
299 }
300 workInProgress = workInProgress.return;
301 } while (workInProgress !== null);
301 - // No Timeout was found. Fallthrough to error mode.
302 + // No boundary was found. Fallthrough to error mode.
303 value = new Error(
304 'An update was suspended, but no placeholder UI was provided.',
305 );
@@ -390,7 +391,7 @@ function unwindWork(
391 popHostContext(workInProgress);
392 return null;
393 }
393 - case TimeoutComponent: {
394 + case PlaceholderComponent: {
395 const effectTag = workInProgress.effectTag;
396 if (effectTag & ShouldCapture) {
397 workInProgress.effectTag = (effectTag & ~ShouldCapture) | DidCapture;
packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js
+71 -76
@@ -3,7 +3,7 @@ let ReactFeatureFlags;
3 let Fragment;
4 let ReactNoop;
5 let SimpleCacheProvider;
6 -let Timeout;
6 +let Placeholder;
7 let StrictMode;
8 let AsyncMode;
9
@@ -22,7 +22,7 @@ describe('ReactSuspense', () => {
22 Fragment = React.Fragment;
23 ReactNoop = require('react-noop-renderer');
24 SimpleCacheProvider = require('simple-cache-provider');
25 - Timeout = React.Timeout;
25 + Placeholder = React.Placeholder;
26 StrictMode = React.StrictMode;
27 AsyncMode = React.unstable_AsyncMode;
28
@@ -89,14 +89,6 @@ describe('ReactSuspense', () => {
89 }
90 }
91
92 - function Fallback(props) {
93 - return (
94 - <Timeout ms={props.timeout}>
95 - {didExpire => (didExpire ? props.placeholder : props.children)}
96 - </Timeout>
97 - );
98 - }
99 -
92 it('suspends rendering and continues later', async () => {
93 function Bar(props) {
94 ReactNoop.yield('Bar');
@@ -106,12 +98,12 @@ describe('ReactSuspense', () => {
98 function Foo() {
99 ReactNoop.yield('Foo');
100 return (
109 - <Fallback>
101 + <Placeholder>
102 <Bar>
103 <AsyncText text="A" ms={100} />
104 <Text text="B" />
105 </Bar>
114 - </Fallback>
106 + </Placeholder>
107 );
108 }
109
@@ -146,15 +138,15 @@ describe('ReactSuspense', () => {
138 });
139
140 it('suspends siblings and later recovers each independently', async () => {
149 - // Render two sibling Timeout components
141 + // Render two sibling Placeholder components
142 ReactNoop.render(
143 <Fragment>
152 - <Fallback timeout={1000} placeholder={<Text text="Loading A..." />}>
144 + <Placeholder delayMs={1000} fallback={<Text text="Loading A..." />}>
145 <AsyncText text="A" ms={5000} />
154 - </Fallback>
155 - <Fallback timeout={3000} placeholder={<Text text="Loading B..." />}>
146 + </Placeholder>
147 + <Placeholder delayMs={3000} fallback={<Text text="Loading B..." />}>
148 <AsyncText text="B" ms={6000} />
157 - </Fallback>
149 + </Placeholder>
150 </Fragment>,
151 );
152 expect(ReactNoop.flush()).toEqual([
@@ -175,15 +167,16 @@ describe('ReactSuspense', () => {
167 span('Loading B...'),
168 ]);
169
178 - // Advance time by enough that the first Timeout's promise resolves
179 - // and switches back to the normal view. The second Timeout should still show the placeholder
170 + // Advance time by enough that the first Placeholder's promise resolves and
171 + // switches back to the normal view. The second Placeholder should still
172 + // show the placeholder
173 ReactNoop.expire(1000);
174 await advanceTimers(1000);
175
176 expect(ReactNoop.flush()).toEqual(['Promise resolved [A]', 'A']);
177 expect(ReactNoop.getChildren()).toEqual([span('A'), span('Loading B...')]);
178
186 - // Advance time by enough that the second Timeout's promise resolves
179 + // Advance time by enough that the second Placeholder's promise resolves
180 // and switches back to the normal view
181 ReactNoop.expire(1000);
182 await advanceTimers(1000);
@@ -194,12 +187,12 @@ describe('ReactSuspense', () => {
187
188 it('continues rendering siblings after suspending', async () => {
189 ReactNoop.render(
197 - <Fallback>
190 + <Placeholder>
191 <Text text="A" />
192 <AsyncText text="B" />
193 <Text text="C" />
194 <Text text="D" />
202 - </Fallback>,
195 + </Placeholder>,
196 );
197 // B suspends. Continue rendering the remaining siblings.
198 expect(ReactNoop.flush()).toEqual(['A', 'Suspend! [B]', 'C', 'D']);
@@ -244,11 +237,11 @@ describe('ReactSuspense', () => {
237 const errorBoundary = React.createRef();
238 function App() {
239 return (
247 - <Fallback>
240 + <Placeholder>
241 <ErrorBoundary ref={errorBoundary}>
242 <AsyncText text="Result" ms={1000} />
243 </ErrorBoundary>
251 - </Fallback>
244 + </Placeholder>
245 );
246 }
247
@@ -306,11 +299,11 @@ describe('ReactSuspense', () => {
299 const errorBoundary = React.createRef();
300 function App() {
301 return (
309 - <Fallback timeout={1000} placeholder={<Text text="Loading..." />}>
302 + <Placeholder delayMs={1000} fallback={<Text text="Loading..." />}>
303 <ErrorBoundary ref={errorBoundary}>
304 <AsyncText text="Result" ms={3000} />
305 </ErrorBoundary>
313 - </Fallback>
306 + </Placeholder>
307 );
308 }
309
@@ -357,10 +350,10 @@ describe('ReactSuspense', () => {
350 it('can update at a higher priority while in a suspended state', async () => {
351 function App(props) {
352 return (
360 - <Fallback>
353 + <Placeholder>
354 <Text text={props.highPri} />
355 <AsyncText text={props.lowPri} />
363 - </Fallback>
356 + </Placeholder>
357 );
358 }
359
@@ -396,10 +389,10 @@ describe('ReactSuspense', () => {
389 it('keeps working on lower priority work after being pinged', async () => {
390 function App(props) {
391 return (
399 - <Fallback>
392 + <Placeholder>
393 <AsyncText text="A" />
394 {props.showB && <Text text="B" />}
402 - </Fallback>
395 + </Placeholder>
396 );
397 }
398
@@ -424,9 +417,9 @@ describe('ReactSuspense', () => {
417 return <Text text="(empty)" />;
418 }
419 return (
427 - <Fallback>
420 + <Placeholder>
421 <AsyncText ms={2000} text="Async" />
429 - </Fallback>
422 + </Placeholder>
423 );
424 }
425
@@ -451,9 +444,9 @@ describe('ReactSuspense', () => {
444 it('forces an expiration after an update times out', async () => {
445 ReactNoop.render(
446 <Fragment>
454 - <Fallback placeholder={<Text text="Loading..." />}>
447 + <Placeholder fallback={<Text text="Loading..." />}>
448 <AsyncText text="Async" ms={20000} />
456 - </Fallback>
449 + </Placeholder>
450 <Text text="Sync" />
451 </Fragment>,
452 );
@@ -489,14 +482,14 @@ describe('ReactSuspense', () => {
482 ReactNoop.render(
483 <Fragment>
484 <Text text="Sync" />
492 - <Fallback timeout={1000} placeholder={<Text text="Loading outer..." />}>
485 + <Placeholder delayMs={1000} fallback={<Text text="Loading outer..." />}>
486 <AsyncText text="Outer content" ms={2000} />
494 - <Fallback
495 - timeout={2500}
496 - placeholder={<Text text="Loading inner..." />}>
487 + <Placeholder
488 + delayMs={2500}
489 + fallback={<Text text="Loading inner..." />}>
490 <AsyncText text="Inner content" ms={5000} />
498 - </Fallback>
499 - </Fallback>
491 + </Placeholder>
492 + </Placeholder>
493 </Fragment>,
494 );
495
@@ -570,9 +563,9 @@ describe('ReactSuspense', () => {
563 ReactNoop.flushSync(() =>
564 ReactNoop.render(
565 <Fragment>
573 - <Fallback placeholder={<Text text="Loading..." />}>
566 + <Placeholder fallback={<Text text="Loading..." />}>
567 <AsyncText text="Async" />
575 - </Fallback>
568 + </Placeholder>
569 <Text text="Sync" />
570 </Fragment>,
571 ),
@@ -598,12 +591,12 @@ describe('ReactSuspense', () => {
591 ReactNoop.flushSync(() =>
592 ReactNoop.render(
593 <Fragment>
601 - <Fallback placeholder={<Text text="Loading (outer)..." />}>
602 - <Fallback placeholder={<AsyncText text="Loading (inner)..." />}>
594 + <Placeholder fallback={<Text text="Loading (outer)..." />}>
595 + <Placeholder fallback={<AsyncText text="Loading (inner)..." />}>
596 <AsyncText text="Async" />
604 - </Fallback>
597 + </Placeholder>
598 <Text text="Sync" />
606 - </Fallback>
599 + </Placeholder>
600 </Fragment>,
601 ),
602 );
@@ -617,12 +610,12 @@ describe('ReactSuspense', () => {
610 expect(ReactNoop.getChildren()).toEqual([span('Loading (outer)...')]);
611 });
612
620 - it('expires early with a `timeout` option', async () => {
613 + it('expires early with a `delayMs` option', async () => {
614 ReactNoop.render(
615 <Fragment>
623 - <Fallback timeout={1000} placeholder={<Text text="Loading..." />}>
616 + <Placeholder delayMs={1000} fallback={<Text text="Loading..." />}>
617 <AsyncText text="Async" ms={3000} />
625 - </Fallback>
618 + </Placeholder>
619 <Text text="Sync" />
620 </Fragment>,
621 );
@@ -654,17 +647,19 @@ describe('ReactSuspense', () => {
647 it('throws a helpful error when an update is suspends without a placeholder', () => {
648 expect(() => {
649 ReactNoop.flushSync(() =>
657 - ReactNoop.render(<Timeout>{() => <AsyncText text="Async" />}</Timeout>),
650 + ReactNoop.render(
651 + <Placeholder>{() => <AsyncText text="Async" />}</Placeholder>,
652 + ),
653 );
654 }).toThrow('An update was suspended, but no placeholder UI was provided.');
655 });
656
662 - it('a Timeout component correctly handles more than one suspended child', async () => {
657 + it('a Placeholder component correctly handles more than one suspended child', async () => {
658 ReactNoop.render(
664 - <Fallback timeout={0}>
659 + <Placeholder delayMs={0}>
660 <AsyncText text="A" ms={100} />
661 <AsyncText text="B" ms={100} />
667 - </Fallback>,
662 + </Placeholder>,
663 );
664 expect(ReactNoop.expire(10000)).toEqual(['Suspend! [A]', 'Suspend! [B]']);
665 expect(ReactNoop.getChildren()).toEqual([]);
@@ -682,9 +677,9 @@ describe('ReactSuspense', () => {
677
678 it('can resume rendering earlier than a timeout', async () => {
679 ReactNoop.render(
685 - <Fallback timeout={1000} placeholder={<Text text="Loading..." />}>
680 + <Placeholder delayMs={1000} fallback={<Text text="Loading..." />}>
681 <AsyncText text="Async" ms={100} />
687 - </Fallback>,
682 + </Placeholder>,
683 );
684 expect(ReactNoop.flush()).toEqual(['Suspend! [Async]', 'Loading...']);
685 expect(ReactNoop.getChildren()).toEqual([]);
@@ -707,13 +702,13 @@ describe('ReactSuspense', () => {
702 it('starts working on an update even if its priority falls between two suspended levels', async () => {
703 function App(props) {
704 return (
710 - <Fallback timeout={10000}>
705 + <Placeholder delayMs={10000}>
706 {props.text === 'C' ? (
707 <Text text="C" />
708 ) : (
709 <AsyncText text={props.text} ms={10000} />
710 )}
716 - </Fallback>
711 + </Placeholder>
712 );
713 }
714
@@ -756,7 +751,7 @@ describe('ReactSuspense', () => {
751 it('can hide a tree to unblock its surroundings', async () => {
752 function App() {
753 return (
759 - <Timeout ms={1000}>
754 + <Placeholder delayMs={1000}>
755 {didTimeout => (
756 <Fragment>
757 <div hidden={didTimeout}>
@@ -765,7 +760,7 @@ describe('ReactSuspense', () => {
760 {didTimeout ? <Text text="Loading..." /> : null}
761 </Fragment>
762 )}
768 - </Timeout>
763 + </Placeholder>
764 );
765 }
766
@@ -789,13 +784,13 @@ describe('ReactSuspense', () => {
784 function Never() {
785 // Throws a promise that resolves after some arbitrarily large
786 // number of seconds. The idea is that this component will never
792 - // resolve. It's always wrapped by a Timeout.
787 + // resolve. It's always wrapped by a Placeholder.
788 throw new Promise(resolve => setTimeout(() => resolve(), 10000));
789 }
790
791 function Delay({ms}) {
792 return (
798 - <Timeout ms={ms}>
793 + <Placeholder delayMs={ms}>
794 {didTimeout => {
795 if (didTimeout) {
796 // Once ms has elapsed, render null. This allows the rest of the
@@ -804,7 +799,7 @@ describe('ReactSuspense', () => {
799 }
800 return <Never />;
801 }}
807 - </Timeout>
802 + </Placeholder>
803 );
804 }
805
@@ -838,9 +833,9 @@ describe('ReactSuspense', () => {
833 it('times out immediately', async () => {
834 function App() {
835 return (
841 - <Fallback timeout={1000} placeholder={<Text text="Loading..." />}>
836 + <Placeholder delayMs={1000} fallback={<Text text="Loading..." />}>
837 <AsyncText ms={100} text="Result" />
843 - </Fallback>
838 + </Placeholder>
839 );
840 }
841
@@ -861,7 +856,7 @@ describe('ReactSuspense', () => {
856 expect(ReactNoop.getChildren()).toEqual([span('Result')]);
857 });
858
864 - it('times out immediately when Timeout is in loose mode, even if the suspender is async', async () => {
859 + it('times out immediately when Placeholder is in loose mode, even if the suspender is async', async () => {
860 class UpdatingText extends React.Component {
861 state = {step: 1};
862 render() {
@@ -869,7 +864,7 @@ describe('ReactSuspense', () => {
864 }
865 }
866
872 - function Placeholder() {
867 + function Spinner() {
868 return (
869 <Fragment>
870 <Text text="Loading (1)" />
@@ -882,12 +877,12 @@ describe('ReactSuspense', () => {
877 const text = React.createRef(null);
878 function App() {
879 return (
885 - <Fallback timeout={1000} placeholder={<Placeholder />}>
880 + <Placeholder delayMs={1000} fallback={<Spinner />}>
881 <AsyncMode>
882 <UpdatingText ref={text} />
883 <Text text="Sibling" />
884 </AsyncMode>
890 - </Fallback>
885 + </Placeholder>
886 );
887 }
888
@@ -959,7 +954,7 @@ describe('ReactSuspense', () => {
954 function App() {
955 return (
956 <StrictMode>
962 - <Fallback timeout={1000} placeholder={<Text text="Loading..." />}>
957 + <Placeholder delayMs={1000} fallback={<Text text="Loading..." />}>
958 <AsyncMode>
959 <UpdatingText ref={text1} initialText="Async: 1">
960 {text => (
@@ -971,7 +966,7 @@ describe('ReactSuspense', () => {
966 )}
967 </UpdatingText>
968 </AsyncMode>
974 - </Fallback>
969 + </Placeholder>
970 <AsyncMode>
971 <UpdatingText ref={text2} initialText="Sync: 1">
972 {text => (
@@ -1091,7 +1086,7 @@ describe('ReactSuspense', () => {
1086 function App() {
1087 return (
1088 <Fragment>
1094 - <Fallback timeout={1000} placeholder={<Text text="Loading..." />}>
1089 + <Placeholder delayMs={1000} fallback={<Text text="Loading..." />}>
1090 <AsyncMode>
1091 <UpdatingText ref={text1} initialText="Async: 1">
1092 {text => (
@@ -1103,7 +1098,7 @@ describe('ReactSuspense', () => {
1098 )}
1099 </UpdatingText>
1100 </AsyncMode>
1106 - </Fallback>
1101 + </Placeholder>
1102 <AsyncMode>
1103 <UpdatingText ref={text2} initialText="Sync: 1">
1104 {text => (
@@ -1235,13 +1230,13 @@ describe('ReactSuspense', () => {
1230
1231 function App() {
1232 return (
1238 - <Fallback
1239 - timeout={1000}
1240 - placeholder={<TextWithLifecycle text="Loading..." />}>
1233 + <Placeholder
1234 + delayMs={1000}
1235 + fallback={<TextWithLifecycle text="Loading..." />}>
1236 <TextWithLifecycle text="A" />
1237 <AsyncTextWithLifecycle ms={100} text="B" />
1238 <TextWithLifecycle text="C" />
1244 - </Fallback>
1239 + </Placeholder>
1240 );
1241 }
1242
packages/react/src/React.js
+2 -2
@@ -12,7 +12,7 @@ import {
12 REACT_FRAGMENT_TYPE,
13 REACT_PROFILER_TYPE,
14 REACT_STRICT_MODE_TYPE,
15 - REACT_TIMEOUT_TYPE,
15 + REACT_PLACEHOLDER_TYPE,
16 } from 'shared/ReactSymbols';
17 import {enableSuspense} from 'shared/ReactFeatureFlags';
18
@@ -71,7 +71,7 @@ const React = {
71 };
72
73 if (enableSuspense) {
74 - React.Timeout = REACT_TIMEOUT_TYPE;
74 + React.Placeholder = REACT_PLACEHOLDER_TYPE;
75 }
76
77 if (__DEV__) {
packages/shared/ReactSymbols.js
+2 -2
@@ -38,8 +38,8 @@ export const REACT_ASYNC_MODE_TYPE = hasSymbol
38 export const REACT_FORWARD_REF_TYPE = hasSymbol
39 ? Symbol.for('react.forward_ref')
40 : 0xead0;
41 -export const REACT_TIMEOUT_TYPE = hasSymbol
42 - ? Symbol.for('react.timeout')
41 +export const REACT_PLACEHOLDER_TYPE = hasSymbol
42 + ? Symbol.for('react.placeholder')
43 : 0xead1;
44
45 const MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
packages/shared/ReactTypeOfWork.js
+1 -1
@@ -42,4 +42,4 @@ export const ContextConsumer = 12;
42 export const ContextProvider = 13;
43 export const ForwardRef = 14;
44 export const Profiler = 15;
45 -export const TimeoutComponent = 16;
45 +export const PlaceholderComponent = 16;
packages/shared/getComponentName.js
+3 -3
@@ -18,7 +18,7 @@ import {
18 REACT_PROFILER_TYPE,
19 REACT_PROVIDER_TYPE,
20 REACT_STRICT_MODE_TYPE,
21 - REACT_TIMEOUT_TYPE,
21 + REACT_PLACEHOLDER_TYPE,
22 } from 'shared/ReactSymbols';
23
24 function getComponentName(fiber: Fiber): string | null {
@@ -44,8 +44,8 @@ function getComponentName(fiber: Fiber): string | null {
44 return 'Context.Provider';
45 case REACT_STRICT_MODE_TYPE:
46 return 'StrictMode';
47 - case REACT_TIMEOUT_TYPE:
48 - return 'Timeout';
47 + case REACT_PLACEHOLDER_TYPE:
48 + return 'Placeholder';
49 }
50 if (typeof type === 'object' && type !== null) {
51 switch (type.$$typeof) {
packages/shared/isValidElementType.js
+2 -2
@@ -15,7 +15,7 @@ import {
15 REACT_PROFILER_TYPE,
16 REACT_PROVIDER_TYPE,
17 REACT_STRICT_MODE_TYPE,
18 - REACT_TIMEOUT_TYPE,
18 + REACT_PLACEHOLDER_TYPE,
19 } from 'shared/ReactSymbols';
20
21 export default function isValidElementType(type: mixed) {
@@ -27,7 +27,7 @@ export default function isValidElementType(type: mixed) {
27 type === REACT_ASYNC_MODE_TYPE ||
28 type === REACT_PROFILER_TYPE ||
29 type === REACT_STRICT_MODE_TYPE ||
30 - type === REACT_TIMEOUT_TYPE ||
30 + type === REACT_PLACEHOLDER_TYPE ||
31 (typeof type === 'object' &&
32 type !== null &&
33 (type.$$typeof === REACT_PROVIDER_TYPE ||