Skip to content

Commit 7c9181f

Browse files
author
Brian Vaughn
committed
Fixed remaining tearing cases
1 parent 99a8fc7 commit 7c9181f

File tree

3 files changed

+32
-25
lines changed

3 files changed

+32
-25
lines changed

packages/react-reconciler/src/ReactFiberHooks.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ import {
7676
} from './SchedulerWithReactIntegration';
7777
import {
7878
getFirstPendingExpirationTime,
79-
getLastPendingExpirationTime,
8079
getWorkInProgressVersion,
8180
markSourceAsDirty,
8281
setPendingExpirationTime,
@@ -1026,13 +1025,22 @@ function useMutableSource<Source, Snapshot>(
10261025
if (!is(snapshot, maybeNewSnapshot)) {
10271026
setSnapshot(maybeNewSnapshot);
10281027

1028+
const currentTime = requestCurrentTimeForUpdate();
1029+
const suspenseConfig = requestCurrentSuspenseConfig();
1030+
const expirationTime = computeExpirationForFiber(
1031+
currentTime,
1032+
fiber,
1033+
suspenseConfig,
1034+
);
1035+
setPendingExpirationTime(root, expirationTime);
1036+
10291037
// If the source mutated between render and now,
10301038
// there may be state updates already scheduled from the old getSnapshot.
10311039
// Those updates should not commit without this value.
10321040
// There is no mechanism currently to associate these updates though,
10331041
// so for now we fall back to synchronously flushing all pending updates.
10341042
// TODO: Improve this later.
1035-
markRootExpiredAtTime(root, getLastPendingExpirationTime(root));
1043+
markRootExpiredAtTime(root, getFirstPendingExpirationTime(root));
10361044
}
10371045
}
10381046
}
@@ -1088,10 +1096,19 @@ function useMutableSource<Source, Snapshot>(
10881096
if (!is(snapshot, maybeNewSnapshot)) {
10891097
setSnapshot(maybeNewSnapshot);
10901098

1099+
const currentTime = requestCurrentTimeForUpdate();
1100+
const suspenseConfig = requestCurrentSuspenseConfig();
1101+
const expirationTime = computeExpirationForFiber(
1102+
currentTime,
1103+
fiber,
1104+
suspenseConfig,
1105+
);
1106+
setPendingExpirationTime(root, expirationTime);
1107+
10911108
// We missed a mutation before committing.
10921109
// It's possible that other components using this source also have pending updates scheduled.
10931110
// In that case, we should ensure they all commit together.
1094-
markRootExpiredAtTime(root, getLastPendingExpirationTime(root));
1111+
markRootExpiredAtTime(root, getFirstPendingExpirationTime(root));
10951112
}
10961113
}
10971114

packages/react-reconciler/src/ReactMutableSource.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ export function clearPendingUpdates(
3030
root: FiberRoot,
3131
expirationTime: ExpirationTime,
3232
): void {
33-
if (root.mutableSourceFirstPendingUpdateTime <= expirationTime) {
33+
if (expirationTime <= root.mutableSourceLastPendingUpdateTime) {
34+
// All updates for this source have been processed.
3435
root.mutableSourceFirstPendingUpdateTime = NoWork;
35-
}
36-
if (root.mutableSourceLastPendingUpdateTime <= expirationTime) {
3736
root.mutableSourceLastPendingUpdateTime = NoWork;
37+
} else if (expirationTime <= root.mutableSourceFirstPendingUpdateTime) {
38+
// The highest priority pending updates have been processed,
39+
// but we don't how many updates remain between the current and lowest priority.
40+
root.mutableSourceFirstPendingUpdateTime = expirationTime - 1;
3841
}
3942
}
4043

@@ -53,18 +56,14 @@ export function setPendingExpirationTime(
5356
// Because we only track one pending update time per root,
5457
// track the lowest priority update.
5558
// It's inclusive of all other pending updates.
56-
root.mutableSourceLastPendingUpdateTime = Math.max(
57-
root.mutableSourceLastPendingUpdateTime,
58-
expirationTime,
59-
);
59+
if (expirationTime > root.mutableSourceLastPendingUpdateTime) {
60+
root.mutableSourceLastPendingUpdateTime = expirationTime;
61+
}
6062

6163
if (root.mutableSourceFirstPendingUpdateTime === NoWork) {
6264
root.mutableSourceFirstPendingUpdateTime = expirationTime;
63-
} else {
64-
root.mutableSourceFirstPendingUpdateTime = Math.min(
65-
root.mutableSourceFirstPendingUpdateTime,
66-
expirationTime,
67-
);
65+
} else if (expirationTime < root.mutableSourceFirstPendingUpdateTime) {
66+
root.mutableSourceFirstPendingUpdateTime = expirationTime;
6867
}
6968
}
7069

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,9 +1385,7 @@ describe('useMutableSource', () => {
13851385
});
13861386
expect(Scheduler).toFlushUntilNextPaint([]);
13871387

1388-
// TODO: This test currently tears.
1389-
// Fill in with correct values once the entanglement issue has been fixed.
1390-
expect(root.getChildrenAsJSX()).toEqual('first: a1, second: a0');
1388+
expect(root.getChildrenAsJSX()).toEqual('first: a1, second: a1');
13911389
});
13921390

13931391
expect(root.getChildrenAsJSX()).toEqual('first: a1, second: a1');
@@ -1481,20 +1479,13 @@ describe('useMutableSource', () => {
14811479
);
14821480
});
14831481

1484-
// TODO: This test currently tears.
1485-
// Fill in with correct values once the entanglement issue has been fixed.
1486-
// Here's the current behavior:
14871482
expect(Scheduler).toHaveYielded([
14881483
// The partial render completes
14891484
'Child: 2',
14901485
'Commit: 2, 2',
14911486

14921487
// Then we start rendering the low priority mutation
14931488
'Parent: 3',
1494-
// But the child never received a mutation event, because it hadn't
1495-
// mounted yet. So the render tears.
1496-
'Child: 2',
1497-
'Commit: Oops, tearing!',
14981489

14991490
// Eventually the child corrects itself, because of the check that
15001491
// occurs when re-subscribing.

0 commit comments

Comments
 (0)