Batch default, continuous and sync lane
This commit is contained in:
parent
fabef7a6b7
commit
128e7c4cf3
|
|
@ -275,17 +275,32 @@ describe('ReactDOMFiberAsync', () => {
|
|||
expect(ops).toEqual([]);
|
||||
});
|
||||
// Only the active updates have flushed
|
||||
expect(container.textContent).toEqual('BC');
|
||||
expect(ops).toEqual(['BC']);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(container.textContent).toEqual('ABC');
|
||||
expect(ops).toEqual(['ABC']);
|
||||
} else {
|
||||
expect(container.textContent).toEqual('BC');
|
||||
expect(ops).toEqual(['BC']);
|
||||
}
|
||||
|
||||
instance.push('D');
|
||||
expect(container.textContent).toEqual('BC');
|
||||
expect(ops).toEqual(['BC']);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
instance.push('D');
|
||||
expect(container.textContent).toEqual('ABC');
|
||||
expect(ops).toEqual(['ABC']);
|
||||
} else {
|
||||
instance.push('D');
|
||||
expect(container.textContent).toEqual('BC');
|
||||
expect(ops).toEqual(['BC']);
|
||||
}
|
||||
|
||||
// Flush the async updates
|
||||
Scheduler.unstable_flushAll();
|
||||
expect(container.textContent).toEqual('ABCD');
|
||||
expect(ops).toEqual(['BC', 'ABCD']);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(ops).toEqual(['ABC', 'ABCD']);
|
||||
} else {
|
||||
expect(ops).toEqual(['BC', 'ABCD']);
|
||||
}
|
||||
});
|
||||
|
||||
// @gate www
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
enableUpdaterTracking,
|
||||
allowConcurrentByDefault,
|
||||
enableTransitionTracing,
|
||||
enableSyncDefaultUpdates,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {isDevToolsPresent} from './ReactFiberDevToolsHook';
|
||||
import {ConcurrentUpdatesByDefaultMode, NoMode} from './ReactTypeOfMode';
|
||||
|
|
@ -135,8 +136,28 @@ let nextRetryLane: Lane = RetryLane1;
|
|||
function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {
|
||||
switch (getHighestPriorityLane(lanes)) {
|
||||
case SyncHydrationLane:
|
||||
if (enableSyncDefaultUpdates) {
|
||||
let ret = SyncHydrationLane;
|
||||
if (lanes & DefaultHydrationLane) {
|
||||
ret |= DefaultHydrationLane;
|
||||
}
|
||||
if (lanes & InputContinuousHydrationLane) {
|
||||
ret |= InputContinuousHydrationLane;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return SyncHydrationLane;
|
||||
case SyncLane:
|
||||
if (enableSyncDefaultUpdates) {
|
||||
let ret = SyncLane;
|
||||
if (lanes & DefaultLane) {
|
||||
ret |= DefaultLane;
|
||||
}
|
||||
if (lanes & InputContinuousLane) {
|
||||
ret |= InputContinuousLane;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return SyncLane;
|
||||
case InputContinuousHydrationLane:
|
||||
return InputContinuousHydrationLane;
|
||||
|
|
@ -251,7 +272,10 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
|
|||
// Default priority updates should not interrupt transition updates. The
|
||||
// only difference between default updates and transition updates is that
|
||||
// default updates do not support refresh transitions.
|
||||
(nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes)
|
||||
// Interrupt transtion if default is batched with sync.
|
||||
(!enableSyncDefaultUpdates &&
|
||||
nextLane === DefaultLane &&
|
||||
(wipLane & TransitionLanes) !== NoLanes)
|
||||
) {
|
||||
// Keep working on the existing in-progress tree. Do not interrupt.
|
||||
return wipLanes;
|
||||
|
|
|
|||
|
|
@ -157,12 +157,18 @@ describe('ReactBlockingMode', () => {
|
|||
}),
|
||||
);
|
||||
|
||||
// Only the second update should have flushed synchronously
|
||||
expect(Scheduler).toHaveYielded(['B1']);
|
||||
expect(root).toMatchRenderedOutput('A0B1');
|
||||
|
||||
// Now flush the first update
|
||||
expect(Scheduler).toFlushAndYield(['A1']);
|
||||
expect(root).toMatchRenderedOutput('A1B1');
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toHaveYielded(['A1', 'B1']);
|
||||
expect(root).toMatchRenderedOutput('A1B1');
|
||||
} else {
|
||||
// Only the second update should have flushed synchronously
|
||||
expect(Scheduler).toHaveYielded(['B1']);
|
||||
expect(root).toMatchRenderedOutput('A0B1');
|
||||
|
||||
// Now flush the first update
|
||||
expect(Scheduler).toFlushAndYield(['A1']);
|
||||
expect(root).toMatchRenderedOutput('A1B1');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,9 +35,17 @@ describe('ReactClassSetStateCallback', () => {
|
|||
expect(Scheduler).toHaveYielded([0]);
|
||||
|
||||
await act(async () => {
|
||||
app.setState({step: 1}, () =>
|
||||
Scheduler.unstable_yieldValue('Callback 1'),
|
||||
);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
React.startTransition(() => {
|
||||
app.setState({step: 1}, () =>
|
||||
Scheduler.unstable_yieldValue('Callback 1'),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
app.setState({step: 1}, () =>
|
||||
Scheduler.unstable_yieldValue('Callback 1'),
|
||||
);
|
||||
}
|
||||
ReactNoop.flushSync(() => {
|
||||
app.setState({step: 2}, () =>
|
||||
Scheduler.unstable_yieldValue('Callback 2'),
|
||||
|
|
|
|||
|
|
@ -339,7 +339,9 @@ describe('ReactExpiration', () => {
|
|||
// Before the update can finish, update again. Even though no time has
|
||||
// advanced, this update should be given a different expiration time than
|
||||
// the currently rendering one. So, C and D should render with 1, not 2.
|
||||
subscribers.forEach(s => s.setState({text: '2'}));
|
||||
React.startTransition(() => {
|
||||
subscribers.forEach(s => s.setState({text: '2'}));
|
||||
});
|
||||
expect(Scheduler).toFlushAndYieldThrough([
|
||||
'1 [C] [render]',
|
||||
'1 [D] [render]',
|
||||
|
|
|
|||
|
|
@ -54,15 +54,21 @@ describe('ReactFlushSync', () => {
|
|||
// The passive effect will schedule a sync update and a normal update.
|
||||
// They should commit in two separate batches. First the sync one.
|
||||
expect(() => {
|
||||
expect(Scheduler).toFlushUntilNextPaint(['1, 0']);
|
||||
expect(Scheduler).toFlushUntilNextPaint(
|
||||
gate(flags => flags.enableSyncDefaultUpdates) ? ['1, 1'] : ['1', '0'],
|
||||
);
|
||||
}).toErrorDev('flushSync was called from inside a lifecycle method');
|
||||
|
||||
// The remaining update is not sync
|
||||
ReactNoop.flushSync();
|
||||
expect(Scheduler).toHaveYielded([]);
|
||||
|
||||
// Now flush it.
|
||||
expect(Scheduler).toFlushUntilNextPaint(['1, 1']);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||
} else {
|
||||
// Now flush it.
|
||||
expect(Scheduler).toFlushUntilNextPaint(['1 / 1']);
|
||||
}
|
||||
});
|
||||
expect(root).toMatchRenderedOutput('1, 1');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -568,9 +568,13 @@ describe('ReactHooks', () => {
|
|||
});
|
||||
};
|
||||
|
||||
// Update at normal priority
|
||||
ReactTestRenderer.unstable_batchedUpdates(() => update(n => n * 100));
|
||||
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
// Update at transition priority
|
||||
React.startTransition(() => update(n => n * 100));
|
||||
} else {
|
||||
// Update at normal priority
|
||||
ReactTestRenderer.unstable_batchedUpdates(() => update(n => n * 100));
|
||||
}
|
||||
// The new state is eagerly computed.
|
||||
expect(Scheduler).toHaveYielded(['Compute state (1 -> 100)']);
|
||||
|
||||
|
|
|
|||
|
|
@ -815,7 +815,13 @@ describe('ReactHooksWithNoopRenderer', () => {
|
|||
ReactNoop.discreteUpdates(() => {
|
||||
setRow(5);
|
||||
});
|
||||
setRow(20);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
React.startTransition(() => {
|
||||
setRow(20);
|
||||
});
|
||||
} else {
|
||||
setRow(20);
|
||||
}
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Up', 'Down']);
|
||||
expect(root).toMatchRenderedOutput(<span prop="Down" />);
|
||||
|
|
@ -955,11 +961,15 @@ describe('ReactHooksWithNoopRenderer', () => {
|
|||
ReactNoop.flushSync(() => {
|
||||
counter.current.dispatch(INCREMENT);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 1']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
|
||||
|
||||
expect(Scheduler).toFlushAndYield(['Count: 4']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 4')]);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toHaveYielded(['Count: 4']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 4')]);
|
||||
} else {
|
||||
expect(Scheduler).toHaveYielded(['Count: 1']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
|
||||
expect(Scheduler).toFlushAndYield(['Count: 4']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 4')]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1717,11 +1727,15 @@ describe('ReactHooksWithNoopRenderer', () => {
|
|||
// As a result we, somewhat surprisingly, commit them in the opposite order.
|
||||
// This should be fine because any non-discrete set of work doesn't guarantee order
|
||||
// and easily could've happened slightly later too.
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Will set count to 1',
|
||||
'Count: 2',
|
||||
'Count: 1',
|
||||
]);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toHaveYielded(['Will set count to 1', 'Count: 1']);
|
||||
} else {
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Will set count to 1',
|
||||
'Count: 2',
|
||||
'Count: 1',
|
||||
]);
|
||||
}
|
||||
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1910,21 +1910,37 @@ describe('ReactIncremental', () => {
|
|||
<ShowBoth />
|
||||
</Intl>,
|
||||
);
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
'ShowLocale {"locale":"sv"}',
|
||||
'ShowBoth {"locale":"sv"}',
|
||||
'Intl {}',
|
||||
'ShowLocale {"locale":"en"}',
|
||||
'Router {}',
|
||||
'Indirection {}',
|
||||
'ShowLocale {"locale":"en"}',
|
||||
'ShowRoute {"route":"/about"}',
|
||||
'ShowNeither {}',
|
||||
'Intl {}',
|
||||
'ShowBoth {"locale":"ru","route":"/about"}',
|
||||
'ShowBoth {"locale":"en","route":"/about"}',
|
||||
'ShowBoth {"locale":"en"}',
|
||||
]);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
'Intl {}',
|
||||
'ShowLocale {"locale":"en"}',
|
||||
'Router {}',
|
||||
'Indirection {}',
|
||||
'ShowLocale {"locale":"en"}',
|
||||
'ShowRoute {"route":"/about"}',
|
||||
'ShowNeither {}',
|
||||
'Intl {}',
|
||||
'ShowBoth {"locale":"ru","route":"/about"}',
|
||||
'ShowBoth {"locale":"en","route":"/about"}',
|
||||
'ShowBoth {"locale":"en"}',
|
||||
]);
|
||||
} else {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
'ShowLocale {"locale":"sv"}',
|
||||
'ShowBoth {"locale":"sv"}',
|
||||
'Intl {}',
|
||||
'ShowLocale {"locale":"en"}',
|
||||
'Router {}',
|
||||
'Indirection {}',
|
||||
'ShowLocale {"locale":"en"}',
|
||||
'ShowRoute {"route":"/about"}',
|
||||
'ShowNeither {}',
|
||||
'Intl {}',
|
||||
'ShowBoth {"locale":"ru","route":"/about"}',
|
||||
'ShowBoth {"locale":"en","route":"/about"}',
|
||||
'ShowBoth {"locale":"en"}',
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it('does not leak own context into context provider', () => {
|
||||
|
|
@ -2758,7 +2774,11 @@ describe('ReactIncremental', () => {
|
|||
// Interrupt at same priority
|
||||
ReactNoop.render(<Parent step={2} />);
|
||||
|
||||
expect(Scheduler).toFlushAndYield(['Child: 1', 'Parent: 2', 'Child: 2']);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toFlushAndYield(['Parent: 2', 'Child: 2']);
|
||||
} else {
|
||||
expect(Scheduler).toFlushAndYield(['Child: 1', 'Parent: 2', 'Child: 2']);
|
||||
}
|
||||
});
|
||||
|
||||
it('does not interrupt for update at lower priority', () => {
|
||||
|
|
@ -2785,7 +2805,11 @@ describe('ReactIncremental', () => {
|
|||
ReactNoop.expire(2000);
|
||||
ReactNoop.render(<Parent step={2} />);
|
||||
|
||||
expect(Scheduler).toFlushAndYield(['Child: 1', 'Parent: 2', 'Child: 2']);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toFlushAndYield(['Parent: 2', 'Child: 2']);
|
||||
} else {
|
||||
expect(Scheduler).toFlushAndYield(['Child: 1', 'Parent: 2', 'Child: 2']);
|
||||
}
|
||||
});
|
||||
|
||||
it('does interrupt for update at higher priority', () => {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ describe('ReactIncrementalUpdates', () => {
|
|||
state = {};
|
||||
componentDidMount() {
|
||||
Scheduler.unstable_yieldValue('commit');
|
||||
ReactNoop.deferredUpdates(() => {
|
||||
React.startTransition(() => {
|
||||
// Has low priority
|
||||
this.setState({b: 'b'});
|
||||
this.setState({c: 'c'});
|
||||
|
|
@ -111,13 +111,13 @@ describe('ReactIncrementalUpdates', () => {
|
|||
expect(Scheduler).toFlushAndYield(['render', 'componentDidMount']);
|
||||
|
||||
ReactNoop.flushSync(() => {
|
||||
ReactNoop.deferredUpdates(() => {
|
||||
React.startTransition(() => {
|
||||
instance.setState({x: 'x'});
|
||||
instance.setState({y: 'y'});
|
||||
});
|
||||
instance.setState({a: 'a'});
|
||||
instance.setState({b: 'b'});
|
||||
ReactNoop.deferredUpdates(() => {
|
||||
React.startTransition(() => {
|
||||
instance.updater.enqueueReplaceState(instance, {c: 'c'});
|
||||
instance.setState({d: 'd'});
|
||||
});
|
||||
|
|
@ -190,27 +190,40 @@ describe('ReactIncrementalUpdates', () => {
|
|||
});
|
||||
|
||||
// The sync updates should have flushed, but not the async ones
|
||||
expect(Scheduler).toHaveYielded(['e', 'f']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('ef')]);
|
||||
expect(Scheduler).toHaveYielded(['d', 'e', 'f']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('def')]);
|
||||
|
||||
// Now flush the remaining work. Even though e and f were already processed,
|
||||
// they should be processed again, to ensure that the terminal state
|
||||
// is deterministic.
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
// Since 'g' is in a transition, we'll process 'd' separately first.
|
||||
// That causes us to process 'd' with 'e' and 'f' rebased.
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
// Then we'll re-process everything for 'g'.
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
]);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
// Then we'll re-process everything for 'g'.
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
]);
|
||||
} else {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
// Since 'g' is in a transition, we'll process 'd' separately first.
|
||||
// That causes us to process 'd' with 'e' and 'f' rebased.
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
// Then we'll re-process everything for 'g'.
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
]);
|
||||
}
|
||||
expect(ReactNoop.getChildren()).toEqual([span('abcdefg')]);
|
||||
} else {
|
||||
instance.setState(createUpdate('d'));
|
||||
|
|
@ -292,27 +305,40 @@ describe('ReactIncrementalUpdates', () => {
|
|||
});
|
||||
|
||||
// The sync updates should have flushed, but not the async ones.
|
||||
expect(Scheduler).toHaveYielded(['e', 'f']);
|
||||
expect(Scheduler).toHaveYielded(['d', 'e', 'f']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('f')]);
|
||||
|
||||
// Now flush the remaining work. Even though e and f were already processed,
|
||||
// they should be processed again, to ensure that the terminal state
|
||||
// is deterministic.
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
// Since 'g' is in a transition, we'll process 'd' separately first.
|
||||
// That causes us to process 'd' with 'e' and 'f' rebased.
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
// Then we'll re-process everything for 'g'.
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
]);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
// Then we'll re-process everything for 'g'.
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
]);
|
||||
} else {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
// Since 'g' is in a transition, we'll process 'd' separately first.
|
||||
// That causes us to process 'd' with 'e' and 'f' rebased.
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
// Then we'll re-process everything for 'g'.
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'g',
|
||||
]);
|
||||
}
|
||||
expect(ReactNoop.getChildren()).toEqual([span('fg')]);
|
||||
} else {
|
||||
instance.setState(createUpdate('d'));
|
||||
|
|
@ -688,21 +714,29 @@ describe('ReactIncrementalUpdates', () => {
|
|||
pushToLog('B'),
|
||||
);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// A and B are pending. B is higher priority, so we'll render that first.
|
||||
'Committed: B',
|
||||
// Because A comes first in the queue, we're now in rebase mode. B must
|
||||
// be rebased on top of A. Also, in a layout effect, we received two new
|
||||
// updates: C and D. C is user-blocking and D is synchronous.
|
||||
//
|
||||
// First render the synchronous update. What we're testing here is that
|
||||
// B *is not dropped* even though it has lower than sync priority. That's
|
||||
// because we already committed it. However, this render should not
|
||||
// include C, because that update wasn't already committed.
|
||||
'Committed: BD',
|
||||
'Committed: BCD',
|
||||
'Committed: ABCD',
|
||||
]);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Committed: B',
|
||||
'Committed: BCD',
|
||||
'Committed: ABCD',
|
||||
]);
|
||||
} else {
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// A and B are pending. B is higher priority, so we'll render that first.
|
||||
'Committed: B',
|
||||
// Because A comes first in the queue, we're now in rebase mode. B must
|
||||
// be rebased on top of A. Also, in a layout effect, we received two new
|
||||
// updates: C and D. C is user-blocking and D is synchronous.
|
||||
//
|
||||
// First render the synchronous update. What we're testing here is that
|
||||
// B *is not dropped* even though it has lower than sync priority. That's
|
||||
// because we already committed it. However, this render should not
|
||||
// include C, because that update wasn't already committed.
|
||||
'Committed: BD',
|
||||
'Committed: BCD',
|
||||
'Committed: ABCD',
|
||||
]);
|
||||
}
|
||||
expect(root).toMatchRenderedOutput('ABCD');
|
||||
});
|
||||
|
||||
|
|
@ -748,21 +782,29 @@ describe('ReactIncrementalUpdates', () => {
|
|||
pushToLog('B'),
|
||||
);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// A and B are pending. B is higher priority, so we'll render that first.
|
||||
'Committed: B',
|
||||
// Because A comes first in the queue, we're now in rebase mode. B must
|
||||
// be rebased on top of A. Also, in a layout effect, we received two new
|
||||
// updates: C and D. C is user-blocking and D is synchronous.
|
||||
//
|
||||
// First render the synchronous update. What we're testing here is that
|
||||
// B *is not dropped* even though it has lower than sync priority. That's
|
||||
// because we already committed it. However, this render should not
|
||||
// include C, because that update wasn't already committed.
|
||||
'Committed: BD',
|
||||
'Committed: BCD',
|
||||
'Committed: ABCD',
|
||||
]);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Committed: B',
|
||||
'Committed: BCD',
|
||||
'Committed: ABCD',
|
||||
]);
|
||||
} else {
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// A and B are pending. B is higher priority, so we'll render that first.
|
||||
'Committed: B',
|
||||
// Because A comes first in the queue, we're now in rebase mode. B must
|
||||
// be rebased on top of A. Also, in a layout effect, we received two new
|
||||
// updates: C and D. C is user-blocking and D is synchronous.
|
||||
//
|
||||
// First render the synchronous update. What we're testing here is that
|
||||
// B *is not dropped* even though it has lower than sync priority. That's
|
||||
// because we already committed it. However, this render should not
|
||||
// include C, because that update wasn't already committed.
|
||||
'Committed: BD',
|
||||
'Committed: BCD',
|
||||
'Committed: ABCD',
|
||||
]);
|
||||
}
|
||||
expect(root).toMatchRenderedOutput('ABCD');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -690,8 +690,15 @@ describe('ReactOffscreen', () => {
|
|||
);
|
||||
|
||||
// Before the inner update can finish, we receive another pair of updates.
|
||||
setOuter(2);
|
||||
setInner(2);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
React.startTransition(() => {
|
||||
setOuter(2);
|
||||
setInner(2);
|
||||
});
|
||||
} else {
|
||||
setOuter(2);
|
||||
setInner(2);
|
||||
}
|
||||
|
||||
// Also, before either of these new updates are processed, the hidden
|
||||
// tree is revealed at high priority.
|
||||
|
|
|
|||
|
|
@ -381,7 +381,9 @@ describe('ReactOffscreen', () => {
|
|||
expect(root).toMatchRenderedOutput(<span hidden={true}>A0</span>);
|
||||
|
||||
await act(async () => {
|
||||
setStep(1);
|
||||
React.startTransition(() => {
|
||||
setStep(1);
|
||||
});
|
||||
ReactNoop.flushSync(() => {
|
||||
setText('B');
|
||||
});
|
||||
|
|
@ -513,8 +515,10 @@ describe('ReactOffscreen', () => {
|
|||
|
||||
// Before the tree commits, schedule a concurrent event. The inner update
|
||||
// is to a tree that's just about to be hidden.
|
||||
setOuter(2);
|
||||
setInner(2);
|
||||
startTransition(() => {
|
||||
setOuter(2);
|
||||
setInner(2);
|
||||
});
|
||||
|
||||
// Commit the previous render.
|
||||
jest.runAllTimers();
|
||||
|
|
|
|||
|
|
@ -934,16 +934,30 @@ describe('ReactTransition', () => {
|
|||
updateNormalPri();
|
||||
});
|
||||
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// Finish transition update.
|
||||
'Normal pri: 0',
|
||||
'Commit',
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// Interrupt transition.
|
||||
'Transition pri: 0',
|
||||
'Normal pri: 1',
|
||||
'Commit',
|
||||
|
||||
// Normal pri update.
|
||||
'Transition pri: 1',
|
||||
'Normal pri: 1',
|
||||
'Commit',
|
||||
]);
|
||||
// Normal pri update.
|
||||
'Transition pri: 1',
|
||||
'Normal pri: 1',
|
||||
'Commit',
|
||||
]);
|
||||
} else {
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// Finish transition update.
|
||||
'Normal pri: 0',
|
||||
'Commit',
|
||||
|
||||
// Normal pri update.
|
||||
'Transition pri: 1',
|
||||
'Normal pri: 1',
|
||||
'Commit',
|
||||
]);
|
||||
}
|
||||
|
||||
expect(root).toMatchRenderedOutput('Transition pri: 1, Normal pri: 1');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1558,8 +1558,15 @@ describe('useMutableSource', () => {
|
|||
expect(Scheduler).toFlushAndYieldThrough(['a0', 'b0']);
|
||||
// Mutate in an event. This schedules a subscription update on a, which
|
||||
// already mounted, but not b, which hasn't subscribed yet.
|
||||
mutateA('a1');
|
||||
mutateB('b1');
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
React.startTransition(() => {
|
||||
mutateA('a1');
|
||||
mutateB('b1');
|
||||
});
|
||||
} else {
|
||||
mutateA('a1');
|
||||
mutateB('b1');
|
||||
}
|
||||
|
||||
// Mutate again at lower priority. This will schedule another subscription
|
||||
// update on a, but not b. When b mounts and subscriptions, the value it
|
||||
|
|
|
|||
|
|
@ -454,7 +454,13 @@ describe('useSubscription', () => {
|
|||
observableA.next('a-2');
|
||||
|
||||
// Update again
|
||||
renderer.update(<Parent observed={observableA} />);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
React.startTransition(() => {
|
||||
renderer.update(<Parent observed={observableA} />);
|
||||
});
|
||||
} else {
|
||||
renderer.update(<Parent observed={observableA} />);
|
||||
}
|
||||
|
||||
// Flush everything and ensure that the correct subscribable is used
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
|
|
|
|||
Loading…
Reference in New Issue