Skip to content

Commit 86a49da

Browse files
committed
Revert change to expiration time rounding
This means you have to account for the start time approximation heuristic when writing Suspense tests, but that's going to be true regardless. When updating the tests, I also made a fix related to offscreen priority. We should never timeout inside a hidden tree.
1 parent 1b835af commit 86a49da

File tree

4 files changed

+37
-49
lines changed

4 files changed

+37
-49
lines changed

packages/react-reconciler/src/ReactFiberExpirationTime.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ export function expirationTimeToMs(expirationTime: ExpirationTime): number {
2929
return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE;
3030
}
3131

32-
function round(num: number, precision: number): number {
33-
return ((num / precision) | 0) * precision;
32+
function ceiling(num: number, precision: number): number {
33+
return (((num / precision) | 0) + 1) * precision;
3434
}
3535

3636
export function computeExpirationBucket(
@@ -40,7 +40,7 @@ export function computeExpirationBucket(
4040
): ExpirationTime {
4141
return (
4242
MAGIC_NUMBER_OFFSET +
43-
round(
43+
ceiling(
4444
currentTime - MAGIC_NUMBER_OFFSET + expirationInMs / UNIT_SIZE,
4545
bucketSizeMs / UNIT_SIZE,
4646
)

packages/react-reconciler/src/ReactFiberPendingPriority.js

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -167,22 +167,7 @@ export function markPingedPriorityLevel(
167167
if (latestSuspendedTime !== NoWork && latestSuspendedTime <= pingedTime) {
168168
const latestPingedTime = root.latestPingedTime;
169169
if (latestPingedTime === NoWork || latestPingedTime < pingedTime) {
170-
// TODO: At one point, we decided we'd always work on the lowest priority
171-
// suspended level. Part of the reasoning was to avoid displaying
172-
// intermediate suspended states, e.g. if you click on two tabs in quick
173-
// succession, only the final tab should render. But we later realized
174-
// that the correct solution to this problem is in user space, e.g. by
175-
// using a setState updater for the lower priority update that refers
176-
// to the most recent high priority value.
177-
//
178-
// The only reason we track the lowest suspended level is so we don't have
179-
// to track *every* suspended level. It's good enough to work on the last
180-
// one. But in case of a ping, we know exactly what level we received, so
181-
// we can go ahead and work on that one.
182-
//
183-
// Consider using the commented-out line instead:
184-
// root.latestPingedTime = pingedTime;
185-
root.latestPingedTime = latestSuspendedTime;
170+
root.latestPingedTime = pingedTime;
186171
}
187172
}
188173
}

packages/react-reconciler/src/ReactFiberUnwindWork.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {
4141
ShouldCapture,
4242
} from 'shared/ReactTypeOfSideEffect';
4343

44-
import {Sync, expirationTimeToMs} from './ReactFiberExpirationTime';
44+
import {Never, Sync, expirationTimeToMs} from './ReactFiberExpirationTime';
4545

4646
import {
4747
enableGetDerivedStateFromCatch,
@@ -173,7 +173,10 @@ export default function<C, CX>(
173173

174174
const expirationTimeMs = expirationTimeToMs(renderExpirationTime);
175175
const startTimeMs = expirationTimeMs - 5000;
176-
const elapsedMs = currentTimeMs - startTimeMs;
176+
let elapsedMs = currentTimeMs - startTimeMs;
177+
if (elapsedMs < 0) {
178+
elapsedMs = 0;
179+
}
177180
const remainingTimeMs = expirationTimeMs - currentTimeMs;
178181

179182
// Find the earliest timeout of all the timeouts in the ancestor path.
@@ -212,7 +215,7 @@ export default function<C, CX>(
212215
// Compute the remaining time until the timeout.
213216
const msUntilTimeout = earliestTimeoutMs - elapsedMs;
214217

215-
if (msUntilTimeout > 0) {
218+
if (renderExpirationTime === Never || msUntilTimeout > 0) {
216219
// There's still time remaining.
217220
suspendRoot(root, thenable, msUntilTimeout, renderExpirationTime);
218221
const onResolveOrReject = () => {

packages/react-reconciler/src/__tests__/ReactSuspense-test.internal.js

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ describe('ReactSuspense', () => {
193193
return (
194194
<Fallback>
195195
<ErrorBoundary ref={errorBoundary}>
196-
<AsyncText text="Result" ms={100} />
196+
<AsyncText text="Result" ms={1000} />
197197
</ErrorBoundary>
198198
</Fallback>
199199
);
@@ -204,8 +204,8 @@ describe('ReactSuspense', () => {
204204
expect(ReactNoop.getChildren()).toEqual([]);
205205

206206
textResourceShouldFail = true;
207-
ReactNoop.expire(100);
208-
await advanceTimers(100);
207+
ReactNoop.expire(1000);
208+
await advanceTimers(1000);
209209
textResourceShouldFail = false;
210210

211211
expect(ReactNoop.flush()).toEqual([
@@ -222,8 +222,8 @@ describe('ReactSuspense', () => {
222222
cache.invalidate();
223223

224224
expect(ReactNoop.flush()).toEqual(['Suspend! [Result]']);
225-
ReactNoop.expire(100);
226-
await advanceTimers(100);
225+
ReactNoop.expire(1000);
226+
await advanceTimers(1000);
227227
expect(ReactNoop.flush()).toEqual(['Promise resolved [Result]', 'Result']);
228228
expect(ReactNoop.getChildren()).toEqual([span('Result')]);
229229
});
@@ -248,9 +248,9 @@ describe('ReactSuspense', () => {
248248
const errorBoundary = React.createRef();
249249
function App() {
250250
return (
251-
<Fallback timeout={50} placeholder={<Text text="Loading..." />}>
251+
<Fallback timeout={1000} placeholder={<Text text="Loading..." />}>
252252
<ErrorBoundary ref={errorBoundary}>
253-
<AsyncText text="Result" ms={100} />
253+
<AsyncText text="Result" ms={3000} />
254254
</ErrorBoundary>
255255
</Fallback>
256256
);
@@ -260,14 +260,14 @@ describe('ReactSuspense', () => {
260260
expect(ReactNoop.flush()).toEqual(['Suspend! [Result]']);
261261
expect(ReactNoop.getChildren()).toEqual([]);
262262

263-
ReactNoop.expire(50);
264-
await advanceTimers(50);
263+
ReactNoop.expire(2000);
264+
await advanceTimers(2000);
265265
expect(ReactNoop.flush()).toEqual(['Suspend! [Result]', 'Loading...']);
266266
expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
267267

268268
textResourceShouldFail = true;
269-
ReactNoop.expire(50);
270-
await advanceTimers(50);
269+
ReactNoop.expire(1000);
270+
await advanceTimers(1000);
271271
textResourceShouldFail = false;
272272

273273
expect(ReactNoop.flush()).toEqual([
@@ -283,9 +283,9 @@ describe('ReactSuspense', () => {
283283
errorBoundary.current.reset();
284284
cache.invalidate();
285285

286-
expect(ReactNoop.flush()).toEqual(['Suspend! [Result]', 'Loading...']);
287-
ReactNoop.expire(100);
288-
await advanceTimers(100);
286+
expect(ReactNoop.flush()).toEqual(['Suspend! [Result]']);
287+
ReactNoop.expire(3000);
288+
await advanceTimers(3000);
289289
expect(ReactNoop.flush()).toEqual(['Promise resolved [Result]', 'Result']);
290290
expect(ReactNoop.getChildren()).toEqual([span('Result')]);
291291
});
@@ -495,8 +495,8 @@ describe('ReactSuspense', () => {
495495

496496
// Expire the outer timeout, but don't expire the inner one.
497497
// We should see the outer loading placeholder.
498-
ReactNoop.expire(1000);
499-
await advanceTimers(1000);
498+
ReactNoop.expire(1500);
499+
await advanceTimers(1500);
500500
expect(ReactNoop.flush()).toEqual([
501501
'Sync',
502502
// Still suspended.
@@ -600,8 +600,8 @@ describe('ReactSuspense', () => {
600600
it('expires early with a `timeout` option', async () => {
601601
ReactNoop.render(
602602
<Fragment>
603-
<Fallback timeout={100} placeholder={<Text text="Loading..." />}>
604-
<AsyncText text="Async" ms={1000} />
603+
<Fallback timeout={1000} placeholder={<Text text="Loading..." />}>
604+
<AsyncText text="Async" ms={3000} />
605605
</Fallback>
606606
<Text text="Sync" />
607607
</Fragment>,
@@ -619,8 +619,8 @@ describe('ReactSuspense', () => {
619619
// Advance both React's virtual time and Jest's timers by enough to trigger
620620
// the timeout, but not by enough to flush the promise or reach the true
621621
// expiration time.
622-
ReactNoop.expire(120);
623-
await advanceTimers(120);
622+
ReactNoop.expire(2000);
623+
await advanceTimers(2000);
624624
expect(ReactNoop.flush()).toEqual([
625625
// Still suspended.
626626
'Suspend! [Async]',
@@ -763,7 +763,7 @@ describe('ReactSuspense', () => {
763763
{didTimeout => (
764764
<Fragment>
765765
<div hidden={didTimeout}>
766-
<AsyncText text="Async" ms={2000} />
766+
<AsyncText text="Async" ms={3000} />
767767
</div>
768768
{didTimeout ? <Text text="Loading..." /> : null}
769769
</Fragment>
@@ -776,8 +776,8 @@ describe('ReactSuspense', () => {
776776
expect(ReactNoop.flush()).toEqual(['Suspend! [Async]']);
777777
expect(ReactNoop.getChildren()).toEqual([]);
778778

779-
ReactNoop.expire(1000);
780-
await advanceTimers(1000);
779+
ReactNoop.expire(2000);
780+
await advanceTimers(2000);
781781
expect(ReactNoop.flush()).toEqual([
782782
'Suspend! [Async]',
783783
'Loading...',
@@ -932,13 +932,13 @@ describe('ReactSuspense', () => {
932932
ReactNoop.flush();
933933
expect(ReactNoop.getChildren()).toEqual([]);
934934

935-
await advanceTimers(999);
936-
ReactNoop.expire(999);
935+
await advanceTimers(800);
936+
ReactNoop.expire(800);
937937
ReactNoop.flush();
938938
expect(ReactNoop.getChildren()).toEqual([]);
939939

940-
await advanceTimers(1);
941-
ReactNoop.expire(1);
940+
await advanceTimers(1000);
941+
ReactNoop.expire(1000);
942942
ReactNoop.flush();
943943
expect(ReactNoop.getChildren()).toEqual([span('A')]);
944944
});

0 commit comments

Comments
 (0)