Compare commits
12 Commits
main
...
ty-merge-d
| Author | SHA1 | Date |
|---|---|---|
|
|
a9537b1249 | |
|
|
0e7bde4290 | |
|
|
38802826ec | |
|
|
cad26da39c | |
|
|
4f7c7d8dea | |
|
|
9d8c26e1d3 | |
|
|
9f894c2c63 | |
|
|
8cf2534491 | |
|
|
287acc7f8e | |
|
|
238402d713 | |
|
|
651346967b | |
|
|
cfed71511a |
|
|
@ -144,6 +144,10 @@ function flushActWork(resolve, reject) {
|
|||
// $FlowFixMe: Flow doesn't know about global Jest object
|
||||
jest.runOnlyPendingTimers();
|
||||
if (Scheduler.unstable_hasPendingWork()) {
|
||||
// Flush scheduled rAF.
|
||||
if (global.flushRequestAnimationFrameQueue) {
|
||||
global.flushRequestAnimationFrameQueue();
|
||||
}
|
||||
// Committing a fallback scheduled additional work. Continue flushing.
|
||||
flushActWork(resolve, reject);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
|
|||
|
||||
// TODO: Remove this deep import when we delete the legacy root API
|
||||
import {ConcurrentMode, NoMode} from 'react-reconciler/src/ReactTypeOfMode';
|
||||
import * as Scheduler from 'scheduler';
|
||||
|
||||
import {
|
||||
prepareToRenderResources,
|
||||
|
|
@ -393,6 +394,11 @@ const localRequestAnimationFrame =
|
|||
typeof requestAnimationFrame === 'function'
|
||||
? requestAnimationFrame
|
||||
: scheduleTimeout;
|
||||
const localCancelAnimationFrame =
|
||||
typeof window !== 'undefined' &&
|
||||
typeof window.cancelAnimationFrame === 'function'
|
||||
? window.cancelAnimationFrame
|
||||
: cancelTimeout;
|
||||
// -------------------
|
||||
// Microtasks
|
||||
// -------------------
|
||||
|
|
@ -408,6 +414,60 @@ export const scheduleMicrotask: any =
|
|||
.catch(handleErrorInNextTick)
|
||||
: scheduleTimeout; // TODO: Determine the best fallback here.
|
||||
|
||||
export const supportsFrameAlignedTask = true;
|
||||
|
||||
type FrameAlignedTask = {|
|
||||
rafNode: AnimationFrameID,
|
||||
schedulerNode: number,
|
||||
task: function,
|
||||
|};
|
||||
|
||||
let currentTask: FrameAlignedTask | null = null;
|
||||
function performFrameAlignedWork() {
|
||||
const constCurrentTask = currentTask;
|
||||
if (constCurrentTask != null) {
|
||||
const task = constCurrentTask.task;
|
||||
localCancelAnimationFrame(constCurrentTask.rafNode);
|
||||
Scheduler.unstable_cancelCallback(constCurrentTask.schedulerNode);
|
||||
currentTask = null;
|
||||
if (task != null) {
|
||||
task();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function scheduleFrameAlignedTask(task: any): any {
|
||||
if (currentTask === null) {
|
||||
const rafNode = localRequestAnimationFrame(performFrameAlignedWork);
|
||||
|
||||
const schedulerNode = Scheduler.unstable_scheduleCallback(
|
||||
Scheduler.unstable_NormalPriority,
|
||||
performFrameAlignedWork,
|
||||
);
|
||||
|
||||
currentTask = {
|
||||
rafNode,
|
||||
schedulerNode,
|
||||
task,
|
||||
};
|
||||
} else {
|
||||
currentTask.task = task;
|
||||
currentTask.schedulerNode = Scheduler.unstable_scheduleCallback(
|
||||
Scheduler.unstable_NormalPriority,
|
||||
performFrameAlignedWork,
|
||||
);
|
||||
}
|
||||
|
||||
return currentTask;
|
||||
}
|
||||
|
||||
export function cancelFrameAlignedTask(task: any) {
|
||||
Scheduler.unstable_cancelCallback(task.schedulerNode);
|
||||
// We don't cancel the rAF in case it gets re-used later.
|
||||
// But clear the task so if it fires and shouldn't run, it won't.
|
||||
task.task = null;
|
||||
}
|
||||
|
||||
function handleErrorInNextTick(error) {
|
||||
setTimeout(() => {
|
||||
throw error;
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ describe('ReactDOMFiberAsync', () => {
|
|||
|
||||
handleChange = e => {
|
||||
const nextValue = e.target.value;
|
||||
requestIdleCallback(() => {
|
||||
React.startTransition(() => {
|
||||
this.setState({
|
||||
asyncValue: nextValue,
|
||||
});
|
||||
|
|
@ -274,18 +274,32 @@ describe('ReactDOMFiberAsync', () => {
|
|||
expect(container.textContent).toEqual('');
|
||||
expect(ops).toEqual([]);
|
||||
});
|
||||
// Only the active updates have flushed
|
||||
expect(container.textContent).toEqual('BC');
|
||||
expect(ops).toEqual(['BC']);
|
||||
|
||||
if (gate(flags => flags.enableUnifiedSyncLane)) {
|
||||
// DefaultUpdates are batched on the sync lane
|
||||
expect(container.textContent).toEqual('ABC');
|
||||
expect(ops).toEqual(['ABC']);
|
||||
} else {
|
||||
// Only the active updates have flushed
|
||||
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.enableUnifiedSyncLane)) {
|
||||
expect(container.textContent).toEqual('ABC');
|
||||
expect(ops).toEqual(['ABC']);
|
||||
} else {
|
||||
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.enableUnifiedSyncLane)) {
|
||||
expect(ops).toEqual(['ABC', 'ABCD']);
|
||||
} else {
|
||||
expect(ops).toEqual(['BC', 'ABCD']);
|
||||
}
|
||||
});
|
||||
|
||||
// @gate www
|
||||
|
|
@ -545,6 +559,406 @@ describe('ReactDOMFiberAsync', () => {
|
|||
// Therefore the form should have been submitted.
|
||||
expect(formSubmitted).toBe(true);
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling
|
||||
it('Unknown update followed by default update is batched, scheduled in a rAF', () => {
|
||||
let setState = null;
|
||||
let counterRef = null;
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
return <p ref={ref}>Count: {count}</p>;
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = undefined;
|
||||
setState(1);
|
||||
// Unknown updates should schedule a rAF.
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
|
||||
window.event = 'test';
|
||||
setState(2);
|
||||
// Default updates after unknown should re-use the scheduled rAF.
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
|
||||
expect(Scheduler).toHaveYielded([]);
|
||||
expect(counterRef.current.textContent).toBe('Count: 0');
|
||||
global.flushRequestAnimationFrameQueue();
|
||||
expect(Scheduler).toHaveYielded(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling
|
||||
it('Unknown update followed by default update is batched, scheduled in a task', () => {
|
||||
let setState = null;
|
||||
let counterRef = null;
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
return <p ref={ref}>Count: {count}</p>;
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = undefined;
|
||||
setState(1);
|
||||
// Unknown updates should schedule a rAF.
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
|
||||
window.event = 'test';
|
||||
setState(2);
|
||||
// Default updates after unknown should re-use the scheduled rAF.
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
|
||||
expect(Scheduler).toHaveYielded([]);
|
||||
expect(counterRef.current.textContent).toBe('Count: 0');
|
||||
|
||||
expect(Scheduler).toFlushAndYield(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling
|
||||
it('Should re-use scheduled rAF, not cancel and schedule anew', () => {
|
||||
let setState = null;
|
||||
let counterRef = null;
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
return <p ref={ref}>Count: {count}</p>;
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = undefined;
|
||||
setState(1);
|
||||
// Unknown updates should schedule a rAF.
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
const firstRaf = global.requestAnimationFrameQueue[0];
|
||||
|
||||
setState(2);
|
||||
// Default updates after unknown should re-use the scheduled rAF.
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
const secondRaf = global.requestAnimationFrameQueue[0];
|
||||
expect(firstRaf).toBe(secondRaf);
|
||||
|
||||
expect(Scheduler).toHaveYielded([]);
|
||||
expect(counterRef.current.textContent).toBe('Count: 0');
|
||||
global.flushRequestAnimationFrameQueue();
|
||||
expect(Scheduler).toHaveYielded(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling
|
||||
it('Default update followed by an unknown update is batched, scheduled in a rAF', () => {
|
||||
let setState = null;
|
||||
let counterRef = null;
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
return <p ref={ref}>Count: {count}</p>;
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = 'test';
|
||||
setState(1);
|
||||
|
||||
// We should schedule a rAF for default updates.
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
|
||||
window.event = undefined;
|
||||
setState(2);
|
||||
// Unknown updates should schedule a rAF.
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
|
||||
expect(Scheduler).toHaveYielded([]);
|
||||
expect(counterRef.current.textContent).toBe('Count: 0');
|
||||
global.flushRequestAnimationFrameQueue();
|
||||
expect(Scheduler).toHaveYielded(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling
|
||||
it('Default update followed by unknown update is batched, scheduled in a task', () => {
|
||||
let setState = null;
|
||||
let counterRef = null;
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
return <p ref={ref}>Count: {count}</p>;
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = 'test';
|
||||
setState(1);
|
||||
|
||||
// We should schedule a rAF for default updates.
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
|
||||
window.event = undefined;
|
||||
setState(2);
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
|
||||
expect(Scheduler).toHaveYielded([]);
|
||||
expect(counterRef.current.textContent).toBe('Count: 0');
|
||||
|
||||
expect(Scheduler).toFlushAndYield(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling || !allowConcurrentByDefault
|
||||
it('When allowConcurrentByDefault is enabled, unknown updates should not be time sliced', () => {
|
||||
let setState = null;
|
||||
let counterRef = null;
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
return <p ref={ref}>Count: {count}</p>;
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container, {
|
||||
unstable_concurrentUpdatesByDefault: true,
|
||||
});
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = undefined;
|
||||
setState(1);
|
||||
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Count: 1']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 1');
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling || !allowConcurrentByDefault
|
||||
it('When allowConcurrentByDefault is enabled, unknown updates should not be time sliced event with default first', () => {
|
||||
let setState = null;
|
||||
let counterRef = null;
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
return <p ref={ref}>Count: {count}</p>;
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container, {
|
||||
unstable_concurrentUpdatesByDefault: true,
|
||||
});
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = 'test';
|
||||
setState(1);
|
||||
|
||||
window.event = undefined;
|
||||
setState(2);
|
||||
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling || !allowConcurrentByDefault
|
||||
it('When allowConcurrentByDefault is enabled, unknown updates should not be time sliced event with default after', () => {
|
||||
let setState = null;
|
||||
let counterRef = null;
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
return <p ref={ref}>Count: {count}</p>;
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container, {
|
||||
unstable_concurrentUpdatesByDefault: true,
|
||||
});
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = undefined;
|
||||
setState(1);
|
||||
|
||||
window.event = 'test';
|
||||
setState(2);
|
||||
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling
|
||||
it('unknown updates should be rescheduled in rAF after a higher priority update', async () => {
|
||||
let setState = null;
|
||||
let counterRef = null;
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
onClick={() => {
|
||||
setCount(c => c + 1);
|
||||
}}>
|
||||
Count: {count}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container, {
|
||||
unstable_concurrentUpdatesByDefault: true,
|
||||
});
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = undefined;
|
||||
setState(1);
|
||||
|
||||
// Dispatch a click event on the button.
|
||||
const firstEvent = document.createEvent('Event');
|
||||
firstEvent.initEvent('click', true, true);
|
||||
counterRef.current.dispatchEvent(firstEvent);
|
||||
|
||||
await null;
|
||||
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
expect(Scheduler).toHaveYielded(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
|
||||
global.flushRequestAnimationFrameQueue();
|
||||
expect(Scheduler).toHaveYielded([]);
|
||||
} else {
|
||||
expect(Scheduler).toHaveYielded(['Count: 1']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 1');
|
||||
|
||||
global.flushRequestAnimationFrameQueue();
|
||||
expect(Scheduler).toHaveYielded(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
}
|
||||
});
|
||||
|
||||
// @gate enableFrameEndScheduling
|
||||
it('unknown updates should be rescheduled in rAF after suspending without a boundary', async () => {
|
||||
let setState = null;
|
||||
let setThrowing = null;
|
||||
let counterRef = null;
|
||||
|
||||
let promise = null;
|
||||
let unsuspend = null;
|
||||
|
||||
function Counter() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const [isThrowing, setThrowingState] = React.useState(false);
|
||||
setThrowing = setThrowingState;
|
||||
const ref = React.useRef();
|
||||
setState = setCount;
|
||||
counterRef = ref;
|
||||
Scheduler.unstable_yieldValue('Count: ' + count);
|
||||
if (isThrowing) {
|
||||
if (promise === null) {
|
||||
promise = new Promise(resolve => {
|
||||
unsuspend = () => {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
Scheduler.unstable_yieldValue('suspending');
|
||||
throw promise;
|
||||
}
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
onClick={() => {
|
||||
setCount(c => c + 1);
|
||||
}}>
|
||||
Count: {count}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
const root = ReactDOMClient.createRoot(container, {
|
||||
unstable_concurrentUpdatesByDefault: true,
|
||||
});
|
||||
act(() => {
|
||||
root.render(<Counter />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 0']);
|
||||
|
||||
window.event = undefined;
|
||||
console.log('set state');
|
||||
setState(1);
|
||||
//expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
global.flushRequestAnimationFrameQueue();
|
||||
expect(Scheduler).toHaveYielded(['Count: 1']);
|
||||
|
||||
setState(2);
|
||||
setThrowing(true);
|
||||
expect(global.requestAnimationFrameQueue.length).toBe(1);
|
||||
global.flushRequestAnimationFrameQueue();
|
||||
expect(Scheduler).toHaveYielded(['Count: 2', 'suspending']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 1');
|
||||
|
||||
unsuspend();
|
||||
// Default update should be scheduled in a rAF.
|
||||
window.event = 'test';
|
||||
setThrowing(false);
|
||||
setState(2);
|
||||
|
||||
global.flushRequestAnimationFrameQueue();
|
||||
expect(Scheduler).toHaveYielded(['Count: 2']);
|
||||
expect(counterRef.current.textContent).toBe('Count: 2');
|
||||
});
|
||||
});
|
||||
|
||||
it('regression test: does not drop passive effects across roots (#17066)', () => {
|
||||
|
|
|
|||
|
|
@ -399,7 +399,12 @@ describe('ReactDOMRoot', () => {
|
|||
expect(container.textContent).toEqual('a');
|
||||
|
||||
expect(Scheduler).toFlushAndYieldThrough(['b']);
|
||||
if (gate(flags => flags.allowConcurrentByDefault)) {
|
||||
if (
|
||||
gate(
|
||||
flags =>
|
||||
flags.allowConcurrentByDefault && !flags.enableFrameEndScheduling,
|
||||
)
|
||||
) {
|
||||
expect(container.textContent).toEqual('a');
|
||||
} else {
|
||||
expect(container.textContent).toEqual('b');
|
||||
|
|
|
|||
|
|
@ -1432,12 +1432,16 @@ describe('ReactDOMServerPartialHydration', () => {
|
|||
|
||||
// While we're part way through the hydration, we update the state.
|
||||
// This will schedule an update on the children of the suspense boundary.
|
||||
expect(() => updateText('Hi')).toErrorDev(
|
||||
expect(() => {
|
||||
act(() => {
|
||||
updateText('Hi');
|
||||
});
|
||||
}).toErrorDev(
|
||||
"Can't perform a React state update on a component that hasn't mounted yet.",
|
||||
);
|
||||
|
||||
// This will throw it away and rerender.
|
||||
expect(Scheduler).toFlushAndYield(['Child', 'Sibling']);
|
||||
expect(Scheduler).toHaveYielded(['Child', 'Sibling']);
|
||||
|
||||
expect(container.textContent).toBe('Hello');
|
||||
|
||||
|
|
|
|||
|
|
@ -430,6 +430,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
|
|||
})
|
||||
: setTimeout,
|
||||
|
||||
supportsFrameAlignedTask: false,
|
||||
scheduleFrameAlignedTask: undefined,
|
||||
cancelFrameAlignedTask: undefined,
|
||||
|
||||
prepareForCommit(): null | Object {
|
||||
return null;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -206,6 +206,17 @@ Set this to true to indicate that your renderer supports `scheduleMicrotask`. We
|
|||
|
||||
Optional. You can proxy this to `queueMicrotask` or its equivalent in your environment.
|
||||
|
||||
#### `supportsFrameAlignedTask`
|
||||
TODO
|
||||
|
||||
### `scheduleFrameAlignedTask(fn)`
|
||||
|
||||
TODO
|
||||
|
||||
#### `cancelFrameAlignedTask(fn)`
|
||||
|
||||
TODO
|
||||
|
||||
#### `isPrimaryRenderer`
|
||||
|
||||
This is a property (not a function) that should be set to `true` if your renderer is the main one on the page. For example, if you're writing a renderer for the Terminal, it makes sense to set it to `true`, but if your renderer is used *on top of* React DOM or some other existing renderer, set it to `false`.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
import type {Lane, Lanes} from './ReactFiberLane.old';
|
||||
import type {Wakeable} from 'shared/ReactTypes';
|
||||
import type {EventPriority} from './ReactEventPriorities.old';
|
||||
|
||||
import {enableDebugTracing} from 'shared/ReactFeatureFlags';
|
||||
|
||||
|
|
@ -62,11 +63,14 @@ function log(...logArgs): void {
|
|||
const REACT_LOGO_STYLE =
|
||||
'background-color: #20232a; color: #61dafb; padding: 0 2px;';
|
||||
|
||||
export function logCommitStarted(lanes: Lanes): void {
|
||||
export function logCommitStarted(
|
||||
lanes: Lanes,
|
||||
updatePriority: EventPriority,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
group(
|
||||
`%c⚛️%c commit%c (${formatLanes(lanes)})`,
|
||||
`%c⚛️%c commit%c (${formatLanes(lanes)}) (${updatePriority})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'',
|
||||
'font-weight: normal;',
|
||||
|
|
@ -133,11 +137,14 @@ export function logComponentSuspended(
|
|||
}
|
||||
}
|
||||
|
||||
export function logLayoutEffectsStarted(lanes: Lanes): void {
|
||||
export function logLayoutEffectsStarted(
|
||||
lanes: Lanes,
|
||||
updatePriority: EventPriority,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
group(
|
||||
`%c⚛️%c layout effects%c (${formatLanes(lanes)})`,
|
||||
`%c⚛️%c layout effects%c (${formatLanes(lanes)}) (${updatePriority})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'',
|
||||
'font-weight: normal;',
|
||||
|
|
@ -154,11 +161,14 @@ export function logLayoutEffectsStopped(): void {
|
|||
}
|
||||
}
|
||||
|
||||
export function logPassiveEffectsStarted(lanes: Lanes): void {
|
||||
export function logPassiveEffectsStarted(
|
||||
lanes: Lanes,
|
||||
updatePriority: EventPriority,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
group(
|
||||
`%c⚛️%c passive effects%c (${formatLanes(lanes)})`,
|
||||
`%c⚛️%c passive effects%c (${formatLanes(lanes)}) (${updatePriority})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'',
|
||||
'font-weight: normal;',
|
||||
|
|
@ -175,11 +185,14 @@ export function logPassiveEffectsStopped(): void {
|
|||
}
|
||||
}
|
||||
|
||||
export function logRenderStarted(lanes: Lanes): void {
|
||||
export function logRenderStarted(
|
||||
lanes: Lanes,
|
||||
updatePriority: EventPriority,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
group(
|
||||
`%c⚛️%c render%c (${formatLanes(lanes)})`,
|
||||
`%c⚛️%c render%c (${formatLanes(lanes)}) (${updatePriority})`,
|
||||
REACT_LOGO_STYLE,
|
||||
'',
|
||||
'font-weight: normal;',
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import {
|
|||
isHigherEventPriority as isHigherEventPriority_new,
|
||||
} from './ReactEventPriorities.new';
|
||||
|
||||
export opaque type EventPriority = number;
|
||||
export type EventPriority = number;
|
||||
|
||||
export const DiscreteEventPriority: EventPriority = enableNewReconciler
|
||||
? (DiscreteEventPriority_new: any)
|
||||
|
|
|
|||
|
|
@ -13,20 +13,24 @@ import {
|
|||
NoLane,
|
||||
SyncLane,
|
||||
InputContinuousLane,
|
||||
DefaultLane,
|
||||
IdleLane,
|
||||
getHighestPriorityLane,
|
||||
includesNonIdleWork,
|
||||
DefaultLane,
|
||||
} from './ReactFiberLane.new';
|
||||
import {enableUnifiedSyncLane} from '../../shared/ReactFeatureFlags';
|
||||
|
||||
export opaque type EventPriority = Lane;
|
||||
// TODO: Ideally this would be opaque but that doesn't work well with
|
||||
// our reconciler fork infra, since these leak into non-reconciler packages.
|
||||
export type EventPriority = number;
|
||||
|
||||
export const NoEventPriority: EventPriority = NoLane;
|
||||
export const DiscreteEventPriority: EventPriority = SyncLane;
|
||||
export const ContinuousEventPriority: EventPriority = InputContinuousLane;
|
||||
export const DefaultEventPriority: EventPriority = DefaultLane;
|
||||
export const ContinuousEventPriority: EventPriority = SyncLane | (2 << 1);
|
||||
export const DefaultEventPriority: EventPriority = SyncLane | (1 << 1);
|
||||
export const IdleEventPriority: EventPriority = IdleLane;
|
||||
|
||||
let currentUpdatePriority: EventPriority = NoLane;
|
||||
let currentUpdatePriority: EventPriority = NoEventPriority;
|
||||
|
||||
export function getCurrentUpdatePriority(): EventPriority {
|
||||
return currentUpdatePriority;
|
||||
|
|
@ -67,10 +71,13 @@ export function isHigherEventPriority(
|
|||
return a !== 0 && a < b;
|
||||
}
|
||||
|
||||
export function lanesToEventPriority(lanes: Lanes): EventPriority {
|
||||
export function lanesToEventPriority(
|
||||
lanes: Lanes,
|
||||
syncUpdatePriority: EventPriority,
|
||||
): EventPriority {
|
||||
const lane = getHighestPriorityLane(lanes);
|
||||
if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
|
||||
return DiscreteEventPriority;
|
||||
return enableUnifiedSyncLane ? syncUpdatePriority : DiscreteEventPriority;
|
||||
}
|
||||
if (!isHigherEventPriority(ContinuousEventPriority, lane)) {
|
||||
return ContinuousEventPriority;
|
||||
|
|
@ -80,3 +87,19 @@ export function lanesToEventPriority(lanes: Lanes): EventPriority {
|
|||
}
|
||||
return IdleEventPriority;
|
||||
}
|
||||
|
||||
export function laneToEventPriority(
|
||||
lane: Lane,
|
||||
syncUpdatePriority: EventPriority,
|
||||
): EventPriority {
|
||||
if (enableUnifiedSyncLane && lane === SyncLane) {
|
||||
return syncUpdatePriority;
|
||||
}
|
||||
if (!enableUnifiedSyncLane && lane === DefaultLane) {
|
||||
return DefaultEventPriority;
|
||||
}
|
||||
if (lane === InputContinuousLane) {
|
||||
return ContinuousEventPriority;
|
||||
}
|
||||
return (lane: any);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,24 @@ import {
|
|||
NoLane,
|
||||
SyncLane,
|
||||
InputContinuousLane,
|
||||
DefaultLane,
|
||||
IdleLane,
|
||||
getHighestPriorityLane,
|
||||
includesNonIdleWork,
|
||||
DefaultLane,
|
||||
} from './ReactFiberLane.old';
|
||||
import {enableUnifiedSyncLane} from '../../shared/ReactFeatureFlags';
|
||||
|
||||
export opaque type EventPriority = Lane;
|
||||
// TODO: Ideally this would be opaque but that doesn't work well with
|
||||
// our reconciler fork infra, since these leak into non-reconciler packages.
|
||||
export type EventPriority = number;
|
||||
|
||||
export const NoEventPriority: EventPriority = NoLane;
|
||||
export const DiscreteEventPriority: EventPriority = SyncLane;
|
||||
export const ContinuousEventPriority: EventPriority = InputContinuousLane;
|
||||
export const DefaultEventPriority: EventPriority = DefaultLane;
|
||||
export const ContinuousEventPriority: EventPriority = SyncLane | (2 << 1);
|
||||
export const DefaultEventPriority: EventPriority = SyncLane | (1 << 1);
|
||||
export const IdleEventPriority: EventPriority = IdleLane;
|
||||
|
||||
let currentUpdatePriority: EventPriority = NoLane;
|
||||
let currentUpdatePriority: EventPriority = NoEventPriority;
|
||||
|
||||
export function getCurrentUpdatePriority(): EventPriority {
|
||||
return currentUpdatePriority;
|
||||
|
|
@ -67,10 +71,13 @@ export function isHigherEventPriority(
|
|||
return a !== 0 && a < b;
|
||||
}
|
||||
|
||||
export function lanesToEventPriority(lanes: Lanes): EventPriority {
|
||||
export function lanesToEventPriority(
|
||||
lanes: Lanes,
|
||||
syncUpdatePriority: EventPriority,
|
||||
): EventPriority {
|
||||
const lane = getHighestPriorityLane(lanes);
|
||||
if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
|
||||
return DiscreteEventPriority;
|
||||
return enableUnifiedSyncLane ? syncUpdatePriority : DiscreteEventPriority;
|
||||
}
|
||||
if (!isHigherEventPriority(ContinuousEventPriority, lane)) {
|
||||
return ContinuousEventPriority;
|
||||
|
|
@ -80,3 +87,19 @@ export function lanesToEventPriority(lanes: Lanes): EventPriority {
|
|||
}
|
||||
return IdleEventPriority;
|
||||
}
|
||||
|
||||
export function laneToEventPriority(
|
||||
lane: Lane,
|
||||
syncUpdatePriority: EventPriority,
|
||||
): EventPriority {
|
||||
if (enableUnifiedSyncLane && lane === SyncLane) {
|
||||
return syncUpdatePriority;
|
||||
}
|
||||
if (!enableUnifiedSyncLane && lane === DefaultLane) {
|
||||
return DefaultEventPriority;
|
||||
}
|
||||
if (lane === InputContinuousLane) {
|
||||
return ContinuousEventPriority;
|
||||
}
|
||||
return (lane: any);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,6 +275,7 @@ import {
|
|||
pushRootMarkerInstance,
|
||||
TransitionTracingMarker,
|
||||
} from './ReactFiberTracingMarkerComponent.new';
|
||||
import {DefaultEventPriority} from './ReactEventPriorities';
|
||||
|
||||
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||
|
||||
|
|
@ -2821,6 +2822,7 @@ function updateDehydratedSuspenseComponent(
|
|||
current,
|
||||
attemptHydrationAtLane,
|
||||
eventTime,
|
||||
DefaultEventPriority,
|
||||
);
|
||||
} else {
|
||||
// We have already tried to ping at a higher priority than we're rendering with
|
||||
|
|
|
|||
|
|
@ -275,6 +275,7 @@ import {
|
|||
pushRootMarkerInstance,
|
||||
TransitionTracingMarker,
|
||||
} from './ReactFiberTracingMarkerComponent.old';
|
||||
import {DefaultEventPriority} from './ReactEventPriorities';
|
||||
|
||||
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||
|
||||
|
|
@ -2821,6 +2822,7 @@ function updateDehydratedSuspenseComponent(
|
|||
current,
|
||||
attemptHydrationAtLane,
|
||||
eventTime,
|
||||
DefaultEventPriority,
|
||||
);
|
||||
} else {
|
||||
// We have already tried to ping at a higher priority than we're rendering with
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ import {readContext, checkIfContextChanged} from './ReactFiberNewContext.new';
|
|||
import {
|
||||
requestEventTime,
|
||||
requestUpdateLane,
|
||||
requestUpdateLane_getUpdatePriority,
|
||||
scheduleUpdateOnFiber,
|
||||
} from './ReactFiberWorkLoop.new';
|
||||
import {logForceUpdateScheduled, logStateUpdateScheduled} from './DebugTracing';
|
||||
|
|
@ -204,6 +205,7 @@ const classComponentUpdater = {
|
|||
const fiber = getInstance(inst);
|
||||
const eventTime = requestEventTime();
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update = createUpdate(eventTime, lane);
|
||||
update.payload = payload;
|
||||
|
|
@ -216,7 +218,7 @@ const classComponentUpdater = {
|
|||
|
||||
const root = enqueueUpdate(fiber, update, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitions(root, fiber, lane);
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +239,7 @@ const classComponentUpdater = {
|
|||
const fiber = getInstance(inst);
|
||||
const eventTime = requestEventTime();
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update = createUpdate(eventTime, lane);
|
||||
update.tag = ReplaceState;
|
||||
|
|
@ -251,7 +254,7 @@ const classComponentUpdater = {
|
|||
|
||||
const root = enqueueUpdate(fiber, update, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitions(root, fiber, lane);
|
||||
}
|
||||
|
||||
|
|
@ -272,6 +275,7 @@ const classComponentUpdater = {
|
|||
const fiber = getInstance(inst);
|
||||
const eventTime = requestEventTime();
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update = createUpdate(eventTime, lane);
|
||||
update.tag = ForceUpdate;
|
||||
|
|
@ -285,7 +289,7 @@ const classComponentUpdater = {
|
|||
|
||||
const root = enqueueUpdate(fiber, update, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitions(root, fiber, lane);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ import {readContext, checkIfContextChanged} from './ReactFiberNewContext.old';
|
|||
import {
|
||||
requestEventTime,
|
||||
requestUpdateLane,
|
||||
requestUpdateLane_getUpdatePriority,
|
||||
scheduleUpdateOnFiber,
|
||||
} from './ReactFiberWorkLoop.old';
|
||||
import {logForceUpdateScheduled, logStateUpdateScheduled} from './DebugTracing';
|
||||
|
|
@ -204,6 +205,7 @@ const classComponentUpdater = {
|
|||
const fiber = getInstance(inst);
|
||||
const eventTime = requestEventTime();
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update = createUpdate(eventTime, lane);
|
||||
update.payload = payload;
|
||||
|
|
@ -216,7 +218,7 @@ const classComponentUpdater = {
|
|||
|
||||
const root = enqueueUpdate(fiber, update, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitions(root, fiber, lane);
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +239,7 @@ const classComponentUpdater = {
|
|||
const fiber = getInstance(inst);
|
||||
const eventTime = requestEventTime();
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update = createUpdate(eventTime, lane);
|
||||
update.tag = ReplaceState;
|
||||
|
|
@ -251,7 +254,7 @@ const classComponentUpdater = {
|
|||
|
||||
const root = enqueueUpdate(fiber, update, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitions(root, fiber, lane);
|
||||
}
|
||||
|
||||
|
|
@ -272,6 +275,7 @@ const classComponentUpdater = {
|
|||
const fiber = getInstance(inst);
|
||||
const eventTime = requestEventTime();
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update = createUpdate(eventTime, lane);
|
||||
update.tag = ForceUpdate;
|
||||
|
|
@ -285,7 +289,7 @@ const classComponentUpdater = {
|
|||
|
||||
const root = enqueueUpdate(fiber, update, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitions(root, fiber, lane);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ import {
|
|||
getCurrentUpdatePriority,
|
||||
setCurrentUpdatePriority,
|
||||
higherEventPriority,
|
||||
DiscreteEventPriority,
|
||||
} from './ReactEventPriorities.new';
|
||||
import {readContext, checkIfContextChanged} from './ReactFiberNewContext.new';
|
||||
import {HostRoot, CacheComponent} from './ReactWorkTags';
|
||||
|
|
@ -101,6 +102,7 @@ import {
|
|||
getWorkInProgressRootRenderLanes,
|
||||
scheduleUpdateOnFiber,
|
||||
requestUpdateLane,
|
||||
requestUpdateLane_getUpdatePriority,
|
||||
requestEventTime,
|
||||
markSkippedUpdateLanes,
|
||||
isInvalidExecutionContextForEventFunction,
|
||||
|
|
@ -1331,7 +1333,6 @@ function useMutableSource<Source, Snapshot>(
|
|||
|
||||
// Record a pending mutable source update with the same expiration time.
|
||||
const lane = requestUpdateLane(fiber);
|
||||
|
||||
markRootMutableRead(root, lane);
|
||||
} catch (error) {
|
||||
// A selector might throw after a source mutation.
|
||||
|
|
@ -1670,7 +1671,13 @@ function checkIfSnapshotChanged<T>(inst: StoreInstance<T>): boolean {
|
|||
function forceStoreRerender(fiber) {
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
DiscreteEventPriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2381,11 +2388,18 @@ function refreshCache<T>(fiber: Fiber, seedKey: ?() => T, seedValue: T) {
|
|||
case HostRoot: {
|
||||
// Schedule an update on the cache boundary to trigger a refresh.
|
||||
const lane = requestUpdateLane(provider);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
const eventTime = requestEventTime();
|
||||
const refreshUpdate = createLegacyQueueUpdate(eventTime, lane);
|
||||
const root = enqueueLegacyQueueUpdate(provider, refreshUpdate, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, provider, lane, eventTime);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
provider,
|
||||
lane,
|
||||
eventTime,
|
||||
updatePriority,
|
||||
);
|
||||
entangleLegacyQueueTransitions(root, provider, lane);
|
||||
}
|
||||
|
||||
|
|
@ -2427,6 +2441,7 @@ function dispatchReducerAction<S, A>(
|
|||
}
|
||||
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update: Update<S, A> = {
|
||||
lane,
|
||||
|
|
@ -2442,7 +2457,7 @@ function dispatchReducerAction<S, A>(
|
|||
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitionUpdate(root, queue, lane);
|
||||
}
|
||||
}
|
||||
|
|
@ -2466,6 +2481,7 @@ function dispatchSetState<S, A>(
|
|||
}
|
||||
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update: Update<S, A> = {
|
||||
lane,
|
||||
|
|
@ -2524,7 +2540,7 @@ function dispatchSetState<S, A>(
|
|||
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitionUpdate(root, queue, lane);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ import {
|
|||
getCurrentUpdatePriority,
|
||||
setCurrentUpdatePriority,
|
||||
higherEventPriority,
|
||||
DiscreteEventPriority,
|
||||
} from './ReactEventPriorities.old';
|
||||
import {readContext, checkIfContextChanged} from './ReactFiberNewContext.old';
|
||||
import {HostRoot, CacheComponent} from './ReactWorkTags';
|
||||
|
|
@ -101,6 +102,7 @@ import {
|
|||
getWorkInProgressRootRenderLanes,
|
||||
scheduleUpdateOnFiber,
|
||||
requestUpdateLane,
|
||||
requestUpdateLane_getUpdatePriority,
|
||||
requestEventTime,
|
||||
markSkippedUpdateLanes,
|
||||
isInvalidExecutionContextForEventFunction,
|
||||
|
|
@ -1331,7 +1333,6 @@ function useMutableSource<Source, Snapshot>(
|
|||
|
||||
// Record a pending mutable source update with the same expiration time.
|
||||
const lane = requestUpdateLane(fiber);
|
||||
|
||||
markRootMutableRead(root, lane);
|
||||
} catch (error) {
|
||||
// A selector might throw after a source mutation.
|
||||
|
|
@ -1670,7 +1671,13 @@ function checkIfSnapshotChanged<T>(inst: StoreInstance<T>): boolean {
|
|||
function forceStoreRerender(fiber) {
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
DiscreteEventPriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2381,11 +2388,18 @@ function refreshCache<T>(fiber: Fiber, seedKey: ?() => T, seedValue: T) {
|
|||
case HostRoot: {
|
||||
// Schedule an update on the cache boundary to trigger a refresh.
|
||||
const lane = requestUpdateLane(provider);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
const eventTime = requestEventTime();
|
||||
const refreshUpdate = createLegacyQueueUpdate(eventTime, lane);
|
||||
const root = enqueueLegacyQueueUpdate(provider, refreshUpdate, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, provider, lane, eventTime);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
provider,
|
||||
lane,
|
||||
eventTime,
|
||||
updatePriority,
|
||||
);
|
||||
entangleLegacyQueueTransitions(root, provider, lane);
|
||||
}
|
||||
|
||||
|
|
@ -2427,6 +2441,7 @@ function dispatchReducerAction<S, A>(
|
|||
}
|
||||
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update: Update<S, A> = {
|
||||
lane,
|
||||
|
|
@ -2442,7 +2457,7 @@ function dispatchReducerAction<S, A>(
|
|||
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitionUpdate(root, queue, lane);
|
||||
}
|
||||
}
|
||||
|
|
@ -2466,6 +2481,7 @@ function dispatchSetState<S, A>(
|
|||
}
|
||||
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
const update: Update<S, A> = {
|
||||
lane,
|
||||
|
|
@ -2524,7 +2540,7 @@ function dispatchSetState<S, A>(
|
|||
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
entangleTransitionUpdate(root, queue, lane);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,3 +21,6 @@ function shim(...args: any): empty {
|
|||
// Test selectors (when unsupported)
|
||||
export const supportsMicrotasks = false;
|
||||
export const scheduleMicrotask = shim;
|
||||
export const supportsFrameAlignedTask = false;
|
||||
export const scheduleFrameAlignedTask = shim;
|
||||
export const cancelFrameAlignedTask = shim;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import {
|
|||
REACT_LAZY_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
import {enableFloat} from 'shared/ReactFeatureFlags';
|
||||
import {DiscreteEventPriority} from './ReactEventPriorities';
|
||||
|
||||
let resolveFamily: RefreshHandler | null = null;
|
||||
let failedBoundaries: WeakSet<Fiber> | null = null;
|
||||
|
|
@ -313,8 +314,15 @@ function scheduleFibersWithFamiliesRecursively(
|
|||
}
|
||||
if (needsRemount || needsRender) {
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
DiscreteEventPriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (child !== null && !needsRemount) {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import {
|
|||
REACT_LAZY_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
import {enableFloat} from 'shared/ReactFeatureFlags';
|
||||
import {DiscreteEventPriority} from './ReactEventPriorities';
|
||||
|
||||
let resolveFamily: RefreshHandler | null = null;
|
||||
let failedBoundaries: WeakSet<Fiber> | null = null;
|
||||
|
|
@ -313,8 +314,15 @@ function scheduleFibersWithFamiliesRecursively(
|
|||
}
|
||||
if (needsRemount || needsRender) {
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
DiscreteEventPriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (child !== null && !needsRemount) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
import type {Fiber, FiberRoot} from './ReactInternalTypes';
|
||||
import type {Transition} from './ReactFiberTracingMarkerComponent.new';
|
||||
import type {ConcurrentUpdate} from './ReactFiberConcurrentUpdates.new';
|
||||
import type {EventPriority} from './ReactEventPriorities.new';
|
||||
|
||||
// TODO: Ideally these types would be opaque but that doesn't work well with
|
||||
// our reconciler fork infra, since these leak into non-reconciler packages.
|
||||
|
|
@ -23,6 +24,8 @@ import {
|
|||
enableUpdaterTracking,
|
||||
allowConcurrentByDefault,
|
||||
enableTransitionTracing,
|
||||
enableFrameEndScheduling,
|
||||
enableUnifiedSyncLane,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {isDevToolsPresent} from './ReactFiberDevToolsHook.new';
|
||||
import {ConcurrentUpdatesByDefaultMode, NoMode} from './ReactTypeOfMode';
|
||||
|
|
@ -81,6 +84,9 @@ export const IdleLane: Lane = /* */ 0b0100000000000000000
|
|||
|
||||
export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;
|
||||
|
||||
// Copied from ReactEventPriorities to avoid cyclic dependencies
|
||||
const DefaultEventPriority = SyncLane | (1 << 1);
|
||||
|
||||
// This function is used for the experimental timeline (react-devtools-timeline)
|
||||
// It should be kept in sync with the Lanes values above.
|
||||
export function getLabelForLane(lane: Lane): string | void {
|
||||
|
|
@ -97,9 +103,6 @@ export function getLabelForLane(lane: Lane): string | void {
|
|||
if (lane & DefaultHydrationLane) {
|
||||
return 'DefaultHydration';
|
||||
}
|
||||
if (lane & DefaultLane) {
|
||||
return 'Default';
|
||||
}
|
||||
if (lane & TransitionHydrationLane) {
|
||||
return 'TransitionHydration';
|
||||
}
|
||||
|
|
@ -247,7 +250,9 @@ 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)
|
||||
(!enableUnifiedSyncLane &&
|
||||
nextLane === DefaultLane &&
|
||||
(wipLane & TransitionLanes) !== NoLanes)
|
||||
) {
|
||||
// Keep working on the existing in-progress tree. Do not interrupt.
|
||||
return wipLanes;
|
||||
|
|
@ -264,7 +269,8 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
|
|||
// and default updates, so they render in the same batch. The only reason
|
||||
// they use separate lanes is because continuous updates should interrupt
|
||||
// transitions, but default updates should not.
|
||||
nextLanes |= pendingLanes & DefaultLane;
|
||||
nextLanes |=
|
||||
pendingLanes & (enableUnifiedSyncLane ? SyncLane : DefaultLane);
|
||||
}
|
||||
|
||||
// Check for entangled lanes and add them to the batch.
|
||||
|
|
@ -325,6 +331,7 @@ export function getMostRecentEventTime(root: FiberRoot, lanes: Lanes): number {
|
|||
return mostRecentEventTime;
|
||||
}
|
||||
|
||||
////
|
||||
function computeExpirationTime(lane: Lane, currentTime: number) {
|
||||
switch (lane) {
|
||||
case SyncLane:
|
||||
|
|
@ -340,8 +347,8 @@ function computeExpirationTime(lane: Lane, currentTime: number) {
|
|||
// expiration times are an important safeguard when starvation
|
||||
// does happen.
|
||||
return currentTime + 250;
|
||||
case DefaultHydrationLane:
|
||||
case DefaultLane:
|
||||
case DefaultHydrationLane:
|
||||
case TransitionHydrationLane:
|
||||
case TransitionLane1:
|
||||
case TransitionLane2:
|
||||
|
|
@ -469,7 +476,8 @@ export function includesOnlyRetries(lanes: Lanes): boolean {
|
|||
return (lanes & RetryLanes) === lanes;
|
||||
}
|
||||
export function includesOnlyNonUrgentLanes(lanes: Lanes): boolean {
|
||||
const UrgentLanes = SyncLane | InputContinuousLane | DefaultLane;
|
||||
const UrgentLanes =
|
||||
SyncLane | InputContinuousLane | (enableUnifiedSyncLane ? 0 : DefaultLane);
|
||||
return (lanes & UrgentLanes) === NoLanes;
|
||||
}
|
||||
export function includesOnlyTransitions(lanes: Lanes): boolean {
|
||||
|
|
@ -481,14 +489,23 @@ export function includesBlockingLane(root: FiberRoot, lanes: Lanes): boolean {
|
|||
allowConcurrentByDefault &&
|
||||
(root.current.mode & ConcurrentUpdatesByDefaultMode) !== NoMode
|
||||
) {
|
||||
// Concurrent updates by default always use time slicing.
|
||||
if (
|
||||
enableFrameEndScheduling &&
|
||||
(lanes & (enableUnifiedSyncLane ? SyncLane : DefaultLane)) !== NoLanes &&
|
||||
root.updatePriority === DefaultEventPriority
|
||||
) {
|
||||
// Unknown updates should flush synchronously, even in concurrent by default.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, concurrent updates by default always use time slicing.
|
||||
return false;
|
||||
}
|
||||
const SyncDefaultLanes =
|
||||
InputContinuousHydrationLane |
|
||||
InputContinuousLane |
|
||||
DefaultHydrationLane |
|
||||
DefaultLane;
|
||||
(enableUnifiedSyncLane ? SyncLane : DefaultLane);
|
||||
return (lanes & SyncDefaultLanes) !== NoLanes;
|
||||
}
|
||||
|
||||
|
|
@ -588,8 +605,13 @@ export function markRootUpdated(
|
|||
root: FiberRoot,
|
||||
updateLane: Lane,
|
||||
eventTime: number,
|
||||
updatePriority: EventPriority,
|
||||
) {
|
||||
root.pendingLanes |= updateLane;
|
||||
if ((updateLane & SyncLane) !== NoLane) {
|
||||
// Only set priority for the sync lane
|
||||
root.updatePriority = updatePriority;
|
||||
}
|
||||
|
||||
// If there are any suspended transitions, it's possible this new update
|
||||
// could unblock them. Clear the suspended lanes so that we can try rendering
|
||||
|
|
@ -618,7 +640,6 @@ export function markRootUpdated(
|
|||
export function markRootSuspended(root: FiberRoot, suspendedLanes: Lanes) {
|
||||
root.suspendedLanes |= suspendedLanes;
|
||||
root.pingedLanes &= ~suspendedLanes;
|
||||
|
||||
// The suspended lanes are no longer CPU-bound. Clear their expiration times.
|
||||
const expirationTimes = root.expirationTimes;
|
||||
let lanes = suspendedLanes;
|
||||
|
|
@ -649,6 +670,8 @@ export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) {
|
|||
|
||||
root.pendingLanes = remainingLanes;
|
||||
|
||||
// TODO: clearing the priority causes priority to be missing in retryTimedOutBoundary
|
||||
|
||||
// Let's try everything again
|
||||
root.suspendedLanes = NoLanes;
|
||||
root.pingedLanes = NoLanes;
|
||||
|
|
@ -752,6 +775,16 @@ export function getBumpedLaneForHydration(
|
|||
case InputContinuousLane:
|
||||
lane = InputContinuousHydrationLane;
|
||||
break;
|
||||
case SyncLane:
|
||||
if (
|
||||
enableUnifiedSyncLane &&
|
||||
root.updatePriority === DefaultEventPriority
|
||||
) {
|
||||
lane = DefaultHydrationLane;
|
||||
} else {
|
||||
lane = NoLane;
|
||||
}
|
||||
break;
|
||||
case DefaultLane:
|
||||
lane = DefaultHydrationLane;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
import type {Fiber, FiberRoot} from './ReactInternalTypes';
|
||||
import type {Transition} from './ReactFiberTracingMarkerComponent.old';
|
||||
import type {ConcurrentUpdate} from './ReactFiberConcurrentUpdates.old';
|
||||
import type {EventPriority} from './ReactEventPriorities.old';
|
||||
|
||||
// TODO: Ideally these types would be opaque but that doesn't work well with
|
||||
// our reconciler fork infra, since these leak into non-reconciler packages.
|
||||
|
|
@ -23,6 +24,8 @@ import {
|
|||
enableUpdaterTracking,
|
||||
allowConcurrentByDefault,
|
||||
enableTransitionTracing,
|
||||
enableFrameEndScheduling,
|
||||
enableUnifiedSyncLane,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {isDevToolsPresent} from './ReactFiberDevToolsHook.old';
|
||||
import {ConcurrentUpdatesByDefaultMode, NoMode} from './ReactTypeOfMode';
|
||||
|
|
@ -81,6 +84,9 @@ export const IdleLane: Lane = /* */ 0b0100000000000000000
|
|||
|
||||
export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;
|
||||
|
||||
// Copied from ReactEventPriorities to avoid cyclic dependencies
|
||||
const DefaultEventPriority = SyncLane | (1 << 1);
|
||||
|
||||
// This function is used for the experimental timeline (react-devtools-timeline)
|
||||
// It should be kept in sync with the Lanes values above.
|
||||
export function getLabelForLane(lane: Lane): string | void {
|
||||
|
|
@ -97,9 +103,6 @@ export function getLabelForLane(lane: Lane): string | void {
|
|||
if (lane & DefaultHydrationLane) {
|
||||
return 'DefaultHydration';
|
||||
}
|
||||
if (lane & DefaultLane) {
|
||||
return 'Default';
|
||||
}
|
||||
if (lane & TransitionHydrationLane) {
|
||||
return 'TransitionHydration';
|
||||
}
|
||||
|
|
@ -247,7 +250,9 @@ 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)
|
||||
(!enableUnifiedSyncLane &&
|
||||
nextLane === DefaultLane &&
|
||||
(wipLane & TransitionLanes) !== NoLanes)
|
||||
) {
|
||||
// Keep working on the existing in-progress tree. Do not interrupt.
|
||||
return wipLanes;
|
||||
|
|
@ -264,7 +269,8 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
|
|||
// and default updates, so they render in the same batch. The only reason
|
||||
// they use separate lanes is because continuous updates should interrupt
|
||||
// transitions, but default updates should not.
|
||||
nextLanes |= pendingLanes & DefaultLane;
|
||||
nextLanes |=
|
||||
pendingLanes & (enableUnifiedSyncLane ? SyncLane : DefaultLane);
|
||||
}
|
||||
|
||||
// Check for entangled lanes and add them to the batch.
|
||||
|
|
@ -325,6 +331,7 @@ export function getMostRecentEventTime(root: FiberRoot, lanes: Lanes): number {
|
|||
return mostRecentEventTime;
|
||||
}
|
||||
|
||||
////
|
||||
function computeExpirationTime(lane: Lane, currentTime: number) {
|
||||
switch (lane) {
|
||||
case SyncLane:
|
||||
|
|
@ -340,8 +347,8 @@ function computeExpirationTime(lane: Lane, currentTime: number) {
|
|||
// expiration times are an important safeguard when starvation
|
||||
// does happen.
|
||||
return currentTime + 250;
|
||||
case DefaultHydrationLane:
|
||||
case DefaultLane:
|
||||
case DefaultHydrationLane:
|
||||
case TransitionHydrationLane:
|
||||
case TransitionLane1:
|
||||
case TransitionLane2:
|
||||
|
|
@ -469,7 +476,8 @@ export function includesOnlyRetries(lanes: Lanes): boolean {
|
|||
return (lanes & RetryLanes) === lanes;
|
||||
}
|
||||
export function includesOnlyNonUrgentLanes(lanes: Lanes): boolean {
|
||||
const UrgentLanes = SyncLane | InputContinuousLane | DefaultLane;
|
||||
const UrgentLanes =
|
||||
SyncLane | InputContinuousLane | (enableUnifiedSyncLane ? 0 : DefaultLane);
|
||||
return (lanes & UrgentLanes) === NoLanes;
|
||||
}
|
||||
export function includesOnlyTransitions(lanes: Lanes): boolean {
|
||||
|
|
@ -481,14 +489,23 @@ export function includesBlockingLane(root: FiberRoot, lanes: Lanes): boolean {
|
|||
allowConcurrentByDefault &&
|
||||
(root.current.mode & ConcurrentUpdatesByDefaultMode) !== NoMode
|
||||
) {
|
||||
// Concurrent updates by default always use time slicing.
|
||||
if (
|
||||
enableFrameEndScheduling &&
|
||||
(lanes & (enableUnifiedSyncLane ? SyncLane : DefaultLane)) !== NoLanes &&
|
||||
root.updatePriority === DefaultEventPriority
|
||||
) {
|
||||
// Unknown updates should flush synchronously, even in concurrent by default.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, concurrent updates by default always use time slicing.
|
||||
return false;
|
||||
}
|
||||
const SyncDefaultLanes =
|
||||
InputContinuousHydrationLane |
|
||||
InputContinuousLane |
|
||||
DefaultHydrationLane |
|
||||
DefaultLane;
|
||||
(enableUnifiedSyncLane ? SyncLane : DefaultLane);
|
||||
return (lanes & SyncDefaultLanes) !== NoLanes;
|
||||
}
|
||||
|
||||
|
|
@ -588,8 +605,13 @@ export function markRootUpdated(
|
|||
root: FiberRoot,
|
||||
updateLane: Lane,
|
||||
eventTime: number,
|
||||
updatePriority: EventPriority,
|
||||
) {
|
||||
root.pendingLanes |= updateLane;
|
||||
if ((updateLane & SyncLane) !== NoLane) {
|
||||
// Only set priority for the sync lane
|
||||
root.updatePriority = updatePriority;
|
||||
}
|
||||
|
||||
// If there are any suspended transitions, it's possible this new update
|
||||
// could unblock them. Clear the suspended lanes so that we can try rendering
|
||||
|
|
@ -618,7 +640,6 @@ export function markRootUpdated(
|
|||
export function markRootSuspended(root: FiberRoot, suspendedLanes: Lanes) {
|
||||
root.suspendedLanes |= suspendedLanes;
|
||||
root.pingedLanes &= ~suspendedLanes;
|
||||
|
||||
// The suspended lanes are no longer CPU-bound. Clear their expiration times.
|
||||
const expirationTimes = root.expirationTimes;
|
||||
let lanes = suspendedLanes;
|
||||
|
|
@ -649,6 +670,8 @@ export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) {
|
|||
|
||||
root.pendingLanes = remainingLanes;
|
||||
|
||||
// TODO: clearing the priority causes priority to be missing in retryTimedOutBoundary
|
||||
|
||||
// Let's try everything again
|
||||
root.suspendedLanes = NoLanes;
|
||||
root.pingedLanes = NoLanes;
|
||||
|
|
@ -752,6 +775,16 @@ export function getBumpedLaneForHydration(
|
|||
case InputContinuousLane:
|
||||
lane = InputContinuousHydrationLane;
|
||||
break;
|
||||
case SyncLane:
|
||||
if (
|
||||
enableUnifiedSyncLane &&
|
||||
root.updatePriority === DefaultEventPriority
|
||||
) {
|
||||
lane = DefaultHydrationLane;
|
||||
} else {
|
||||
lane = NoLane;
|
||||
}
|
||||
break;
|
||||
case DefaultLane:
|
||||
lane = DefaultHydrationLane;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ import {
|
|||
import {
|
||||
requestEventTime,
|
||||
requestUpdateLane,
|
||||
requestUpdateLane_getUpdatePriority,
|
||||
scheduleUpdateOnFiber,
|
||||
scheduleInitialHydrationOnRoot,
|
||||
flushRoot,
|
||||
|
|
@ -87,6 +88,7 @@ import {
|
|||
NoTimestamp,
|
||||
getHighestPriorityPendingLanes,
|
||||
higherPriorityLane,
|
||||
NoLane,
|
||||
} from './ReactFiberLane.new';
|
||||
import {
|
||||
getCurrentUpdatePriority,
|
||||
|
|
@ -99,6 +101,10 @@ import {
|
|||
findHostInstancesForRefresh,
|
||||
} from './ReactFiberHotReloading.new';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import {
|
||||
DefaultEventPriority,
|
||||
DiscreteEventPriority,
|
||||
} from './ReactEventPriorities';
|
||||
export {registerMutableSourceForHydration} from './ReactMutableSource.new';
|
||||
export {createPortal} from './ReactPortal';
|
||||
export {
|
||||
|
|
@ -330,6 +336,7 @@ export function updateContainer(
|
|||
const current = container.current;
|
||||
const eventTime = requestEventTime();
|
||||
const lane = requestUpdateLane(current);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
if (enableSchedulingProfiler) {
|
||||
markRenderScheduled(lane);
|
||||
|
|
@ -380,7 +387,7 @@ export function updateContainer(
|
|||
|
||||
const root = enqueueUpdate(current, update, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, current, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, current, lane, eventTime, updatePriority);
|
||||
entangleTransitions(root, current, lane);
|
||||
}
|
||||
|
||||
|
|
@ -419,6 +426,9 @@ export function attemptSynchronousHydration(fiber: Fiber): void {
|
|||
if (isRootDehydrated(root)) {
|
||||
// Flush the first scheduled "update".
|
||||
const lanes = getHighestPriorityPendingLanes(root);
|
||||
if ((lanes & SyncLane) !== NoLane) {
|
||||
root.updatePriority = DiscreteEventPriority;
|
||||
}
|
||||
flushRoot(root, lanes);
|
||||
}
|
||||
break;
|
||||
|
|
@ -428,7 +438,13 @@ export function attemptSynchronousHydration(fiber: Fiber): void {
|
|||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, eventTime);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
eventTime,
|
||||
DiscreteEventPriority,
|
||||
);
|
||||
}
|
||||
});
|
||||
// If we're still blocked after this, we need to increase
|
||||
|
|
@ -472,7 +488,7 @@ export function attemptDiscreteHydration(fiber: Fiber): void {
|
|||
const root = enqueueConcurrentRenderForLane(fiber, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, DiscreteEventPriority);
|
||||
}
|
||||
markRetryLaneIfNotHydrated(fiber, lane);
|
||||
}
|
||||
|
|
@ -489,7 +505,7 @@ export function attemptContinuousHydration(fiber: Fiber): void {
|
|||
const root = enqueueConcurrentRenderForLane(fiber, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, DefaultEventPriority);
|
||||
}
|
||||
markRetryLaneIfNotHydrated(fiber, lane);
|
||||
}
|
||||
|
|
@ -501,10 +517,11 @@ export function attemptHydrationAtCurrentPriority(fiber: Fiber): void {
|
|||
return;
|
||||
}
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
const root = enqueueConcurrentRenderForLane(fiber, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
}
|
||||
markRetryLaneIfNotHydrated(fiber, lane);
|
||||
}
|
||||
|
|
@ -683,7 +700,13 @@ if (__DEV__) {
|
|||
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -707,7 +730,13 @@ if (__DEV__) {
|
|||
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -732,7 +761,13 @@ if (__DEV__) {
|
|||
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -745,7 +780,13 @@ if (__DEV__) {
|
|||
}
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
};
|
||||
overridePropsDeletePath = (fiber: Fiber, path: Array<string | number>) => {
|
||||
|
|
@ -755,7 +796,13 @@ if (__DEV__) {
|
|||
}
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
};
|
||||
overridePropsRenamePath = (
|
||||
|
|
@ -769,14 +816,26 @@ if (__DEV__) {
|
|||
}
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
scheduleUpdate = (fiber: Fiber) => {
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ import {
|
|||
import {
|
||||
requestEventTime,
|
||||
requestUpdateLane,
|
||||
requestUpdateLane_getUpdatePriority,
|
||||
scheduleUpdateOnFiber,
|
||||
scheduleInitialHydrationOnRoot,
|
||||
flushRoot,
|
||||
|
|
@ -87,6 +88,7 @@ import {
|
|||
NoTimestamp,
|
||||
getHighestPriorityPendingLanes,
|
||||
higherPriorityLane,
|
||||
NoLane,
|
||||
} from './ReactFiberLane.old';
|
||||
import {
|
||||
getCurrentUpdatePriority,
|
||||
|
|
@ -99,6 +101,10 @@ import {
|
|||
findHostInstancesForRefresh,
|
||||
} from './ReactFiberHotReloading.old';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import {
|
||||
DefaultEventPriority,
|
||||
DiscreteEventPriority,
|
||||
} from './ReactEventPriorities';
|
||||
export {registerMutableSourceForHydration} from './ReactMutableSource.old';
|
||||
export {createPortal} from './ReactPortal';
|
||||
export {
|
||||
|
|
@ -330,6 +336,7 @@ export function updateContainer(
|
|||
const current = container.current;
|
||||
const eventTime = requestEventTime();
|
||||
const lane = requestUpdateLane(current);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
|
||||
if (enableSchedulingProfiler) {
|
||||
markRenderScheduled(lane);
|
||||
|
|
@ -380,7 +387,7 @@ export function updateContainer(
|
|||
|
||||
const root = enqueueUpdate(current, update, lane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, current, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, current, lane, eventTime, updatePriority);
|
||||
entangleTransitions(root, current, lane);
|
||||
}
|
||||
|
||||
|
|
@ -419,6 +426,9 @@ export function attemptSynchronousHydration(fiber: Fiber): void {
|
|||
if (isRootDehydrated(root)) {
|
||||
// Flush the first scheduled "update".
|
||||
const lanes = getHighestPriorityPendingLanes(root);
|
||||
if ((lanes & SyncLane) !== NoLane) {
|
||||
root.updatePriority = DiscreteEventPriority;
|
||||
}
|
||||
flushRoot(root, lanes);
|
||||
}
|
||||
break;
|
||||
|
|
@ -428,7 +438,13 @@ export function attemptSynchronousHydration(fiber: Fiber): void {
|
|||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, eventTime);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
eventTime,
|
||||
DiscreteEventPriority,
|
||||
);
|
||||
}
|
||||
});
|
||||
// If we're still blocked after this, we need to increase
|
||||
|
|
@ -472,7 +488,7 @@ export function attemptDiscreteHydration(fiber: Fiber): void {
|
|||
const root = enqueueConcurrentRenderForLane(fiber, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, DiscreteEventPriority);
|
||||
}
|
||||
markRetryLaneIfNotHydrated(fiber, lane);
|
||||
}
|
||||
|
|
@ -489,7 +505,7 @@ export function attemptContinuousHydration(fiber: Fiber): void {
|
|||
const root = enqueueConcurrentRenderForLane(fiber, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, DefaultEventPriority);
|
||||
}
|
||||
markRetryLaneIfNotHydrated(fiber, lane);
|
||||
}
|
||||
|
|
@ -501,10 +517,11 @@ export function attemptHydrationAtCurrentPriority(fiber: Fiber): void {
|
|||
return;
|
||||
}
|
||||
const lane = requestUpdateLane(fiber);
|
||||
const updatePriority = requestUpdateLane_getUpdatePriority();
|
||||
const root = enqueueConcurrentRenderForLane(fiber, lane);
|
||||
if (root !== null) {
|
||||
const eventTime = requestEventTime();
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||||
scheduleUpdateOnFiber(root, fiber, lane, eventTime, updatePriority);
|
||||
}
|
||||
markRetryLaneIfNotHydrated(fiber, lane);
|
||||
}
|
||||
|
|
@ -683,7 +700,13 @@ if (__DEV__) {
|
|||
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -707,7 +730,13 @@ if (__DEV__) {
|
|||
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -732,7 +761,13 @@ if (__DEV__) {
|
|||
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -745,7 +780,13 @@ if (__DEV__) {
|
|||
}
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
};
|
||||
overridePropsDeletePath = (fiber: Fiber, path: Array<string | number>) => {
|
||||
|
|
@ -755,7 +796,13 @@ if (__DEV__) {
|
|||
}
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
};
|
||||
overridePropsRenamePath = (
|
||||
|
|
@ -769,14 +816,26 @@ if (__DEV__) {
|
|||
}
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
scheduleUpdate = (fiber: Fiber) => {
|
||||
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||||
if (root !== null) {
|
||||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||||
scheduleUpdateOnFiber(
|
||||
root,
|
||||
fiber,
|
||||
SyncLane,
|
||||
NoTimestamp,
|
||||
root.updatePriority,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ import type {Container} from './ReactFiberHostConfig';
|
|||
import {noTimeout, supportsHydration} from './ReactFiberHostConfig';
|
||||
import {createHostRootFiber} from './ReactFiber.new';
|
||||
import {
|
||||
NoLane,
|
||||
NoLanes,
|
||||
NoTimestamp,
|
||||
TotalLanes,
|
||||
createLaneMap,
|
||||
} from './ReactFiberLane.new';
|
||||
import {NoEventPriority} from './ReactEventPriorities.new';
|
||||
import {
|
||||
enableSuspenseCallback,
|
||||
enableCache,
|
||||
|
|
@ -61,7 +61,8 @@ function FiberRootNode(
|
|||
this.context = null;
|
||||
this.pendingContext = null;
|
||||
this.callbackNode = null;
|
||||
this.callbackPriority = NoLane;
|
||||
this.callbackPriority = NoEventPriority;
|
||||
this.updatePriority = NoEventPriority;
|
||||
this.eventTimes = createLaneMap(NoLanes);
|
||||
this.expirationTimes = createLaneMap(NoTimestamp);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ import type {Container} from './ReactFiberHostConfig';
|
|||
import {noTimeout, supportsHydration} from './ReactFiberHostConfig';
|
||||
import {createHostRootFiber} from './ReactFiber.old';
|
||||
import {
|
||||
NoLane,
|
||||
NoLanes,
|
||||
NoTimestamp,
|
||||
TotalLanes,
|
||||
createLaneMap,
|
||||
} from './ReactFiberLane.old';
|
||||
import {NoEventPriority} from './ReactEventPriorities.old';
|
||||
import {
|
||||
enableSuspenseCallback,
|
||||
enableCache,
|
||||
|
|
@ -61,7 +61,8 @@ function FiberRootNode(
|
|||
this.context = null;
|
||||
this.pendingContext = null;
|
||||
this.callbackNode = null;
|
||||
this.callbackPriority = NoLane;
|
||||
this.callbackPriority = NoEventPriority;
|
||||
this.updatePriority = NoEventPriority;
|
||||
this.eventTimes = createLaneMap(NoLanes);
|
||||
this.expirationTimes = createLaneMap(NoTimestamp);
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import {
|
|||
enableDebugTracing,
|
||||
enableLazyContextPropagation,
|
||||
enableUpdaterTracking,
|
||||
enableUnifiedSyncLane,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {createCapturedValueAtFiber} from './ReactCapturedValue';
|
||||
import {
|
||||
|
|
@ -77,6 +78,7 @@ import {
|
|||
markDidThrowWhileHydratingDEV,
|
||||
queueHydrationError,
|
||||
} from './ReactFiberHydrationContext.new';
|
||||
import {DiscreteEventPriority} from './ReactEventPriorities.new';
|
||||
|
||||
function createRootErrorUpdate(
|
||||
fiber: Fiber,
|
||||
|
|
@ -421,8 +423,15 @@ function throwException(
|
|||
} else {
|
||||
// No boundary was found. Unless this is a sync update, this is OK.
|
||||
// We can suspend and wait for more data to arrive.
|
||||
|
||||
if (!includesSyncLane(rootRenderLanes)) {
|
||||
if (
|
||||
!(
|
||||
includesSyncLane(rootRenderLanes) &&
|
||||
!(
|
||||
enableUnifiedSyncLane &&
|
||||
root.updatePriority !== DiscreteEventPriority
|
||||
)
|
||||
)
|
||||
) {
|
||||
// This is not a sync update. Suspend. Since we're not activating a
|
||||
// Suspense boundary, this will unwind all the way to the root without
|
||||
// performing a second pass to render a fallback. (This is arguably how
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import {
|
|||
enableDebugTracing,
|
||||
enableLazyContextPropagation,
|
||||
enableUpdaterTracking,
|
||||
enableUnifiedSyncLane,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {createCapturedValueAtFiber} from './ReactCapturedValue';
|
||||
import {
|
||||
|
|
@ -77,6 +78,7 @@ import {
|
|||
markDidThrowWhileHydratingDEV,
|
||||
queueHydrationError,
|
||||
} from './ReactFiberHydrationContext.old';
|
||||
import {DiscreteEventPriority} from './ReactEventPriorities.old';
|
||||
|
||||
function createRootErrorUpdate(
|
||||
fiber: Fiber,
|
||||
|
|
@ -421,8 +423,15 @@ function throwException(
|
|||
} else {
|
||||
// No boundary was found. Unless this is a sync update, this is OK.
|
||||
// We can suspend and wait for more data to arrive.
|
||||
|
||||
if (!includesSyncLane(rootRenderLanes)) {
|
||||
if (
|
||||
!(
|
||||
includesSyncLane(rootRenderLanes) &&
|
||||
!(
|
||||
enableUnifiedSyncLane &&
|
||||
root.updatePriority !== DiscreteEventPriority
|
||||
)
|
||||
)
|
||||
) {
|
||||
// This is not a sync update. Suspend. Since we're not activating a
|
||||
// Suspense boundary, this will unwind all the way to the root without
|
||||
// performing a second pass to render a fallback. (This is arguably how
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ import {
|
|||
enableUpdaterTracking,
|
||||
enableCache,
|
||||
enableTransitionTracing,
|
||||
enableFrameEndScheduling,
|
||||
enableUnifiedSyncLane,
|
||||
useModernStrictMode,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
|
|
@ -86,8 +88,10 @@ import {
|
|||
scheduleMicrotask,
|
||||
prepareRendererToRender,
|
||||
resetRendererAfterRender,
|
||||
cancelFrameAlignedTask,
|
||||
scheduleFrameAlignedTask,
|
||||
supportsFrameAlignedTask,
|
||||
} from './ReactFiberHostConfig';
|
||||
|
||||
import {
|
||||
createWorkInProgress,
|
||||
assignFiberPropertiesInDEV,
|
||||
|
|
@ -163,16 +167,20 @@ import {
|
|||
movePendingFibersToMemoized,
|
||||
addTransitionToLanesMap,
|
||||
getTransitionsForLanes,
|
||||
InputContinuousLane,
|
||||
DefaultLane,
|
||||
} from './ReactFiberLane.new';
|
||||
import {
|
||||
DiscreteEventPriority,
|
||||
ContinuousEventPriority,
|
||||
DefaultEventPriority,
|
||||
NoEventPriority,
|
||||
IdleEventPriority,
|
||||
getCurrentUpdatePriority,
|
||||
setCurrentUpdatePriority,
|
||||
lowerEventPriority,
|
||||
lanesToEventPriority,
|
||||
laneToEventPriority,
|
||||
} from './ReactEventPriorities.new';
|
||||
import {requestCurrentTransition, NoTransition} from './ReactFiberTransition';
|
||||
import {beginWork as originalBeginWork} from './ReactFiberBeginWork.new';
|
||||
|
|
@ -599,10 +607,17 @@ export function getCurrentTime(): number {
|
|||
return now();
|
||||
}
|
||||
|
||||
let currentUpdatePriority = NoEventPriority;
|
||||
|
||||
export function requestUpdateLane_getUpdatePriority(): EventPriority {
|
||||
return currentUpdatePriority;
|
||||
}
|
||||
|
||||
export function requestUpdateLane(fiber: Fiber): Lane {
|
||||
// Special cases
|
||||
const mode = fiber.mode;
|
||||
if ((mode & ConcurrentMode) === NoMode) {
|
||||
currentUpdatePriority = DiscreteEventPriority;
|
||||
return (SyncLane: Lane);
|
||||
} else if (
|
||||
!deferRenderPhaseUpdateToNextBatch &&
|
||||
|
|
@ -618,7 +633,11 @@ export function requestUpdateLane(fiber: Fiber): Lane {
|
|||
// This behavior is only a fallback. The flag only exists until we can roll
|
||||
// out the setState warning, since existing code might accidentally rely on
|
||||
// the current behavior.
|
||||
return pickArbitraryLane(workInProgressRootRenderLanes);
|
||||
const nextLane = pickArbitraryLane(workInProgressRootRenderLanes);
|
||||
if ((nextLane & SyncLane) === NoLane) {
|
||||
currentUpdatePriority = NoEventPriority;
|
||||
}
|
||||
return nextLane;
|
||||
}
|
||||
|
||||
const isTransition = requestCurrentTransition() !== NoTransition;
|
||||
|
|
@ -642,28 +661,42 @@ export function requestUpdateLane(fiber: Fiber): Lane {
|
|||
// All transitions within the same event are assigned the same lane.
|
||||
currentEventTransitionLane = claimNextTransitionLane();
|
||||
}
|
||||
if ((currentEventTransitionLane & SyncLane) === NoLane) {
|
||||
currentUpdatePriority = NoEventPriority;
|
||||
}
|
||||
|
||||
return currentEventTransitionLane;
|
||||
}
|
||||
|
||||
// Updates originating inside certain React methods, like flushSync, have
|
||||
// their priority set by tracking it with a context variable.
|
||||
//
|
||||
// The opaque type returned by the host config is internally a lane, so we can
|
||||
// use that directly.
|
||||
// TODO: Move this type conversion to the event priority module.
|
||||
const updateLane: Lane = (getCurrentUpdatePriority(): any);
|
||||
if (updateLane !== NoLane) {
|
||||
return updateLane;
|
||||
const updatePriority = getCurrentUpdatePriority();
|
||||
currentUpdatePriority = updatePriority;
|
||||
if (updatePriority !== NoEventPriority) {
|
||||
if (updatePriority === DefaultEventPriority) {
|
||||
return enableUnifiedSyncLane ? SyncLane : DefaultLane;
|
||||
}
|
||||
if (updatePriority === ContinuousEventPriority) {
|
||||
return InputContinuousLane;
|
||||
}
|
||||
return (updatePriority: any);
|
||||
}
|
||||
|
||||
// This update originated outside React. Ask the host environment for an
|
||||
// appropriate priority, based on the type of event.
|
||||
//
|
||||
// The opaque type returned by the host config is internally a lane, so we can
|
||||
// use that directly.
|
||||
// TODO: Move this type conversion to the event priority module.
|
||||
const eventLane: Lane = (getCurrentEventPriority(): any);
|
||||
return eventLane;
|
||||
const eventPriority = getCurrentEventPriority();
|
||||
currentUpdatePriority = eventPriority;
|
||||
if (eventPriority === DefaultEventPriority) {
|
||||
return enableUnifiedSyncLane ? SyncLane : DefaultLane;
|
||||
}
|
||||
if (eventPriority === ContinuousEventPriority) {
|
||||
return InputContinuousLane;
|
||||
}
|
||||
return (eventPriority: any);
|
||||
}
|
||||
|
||||
function requestRetryLane(fiber: Fiber) {
|
||||
|
|
@ -674,8 +707,10 @@ function requestRetryLane(fiber: Fiber) {
|
|||
// Special cases
|
||||
const mode = fiber.mode;
|
||||
if ((mode & ConcurrentMode) === NoMode) {
|
||||
currentUpdatePriority = DiscreteEventPriority;
|
||||
return (SyncLane: Lane);
|
||||
}
|
||||
currentUpdatePriority = DefaultEventPriority;
|
||||
|
||||
return claimNextRetryLane();
|
||||
}
|
||||
|
|
@ -685,6 +720,7 @@ export function scheduleUpdateOnFiber(
|
|||
fiber: Fiber,
|
||||
lane: Lane,
|
||||
eventTime: number,
|
||||
updatePriority: EventPriority,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (isRunningInsertionEffect) {
|
||||
|
|
@ -697,9 +733,8 @@ export function scheduleUpdateOnFiber(
|
|||
didScheduleUpdateDuringPassiveEffects = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark that the root has a pending update.
|
||||
markRootUpdated(root, lane, eventTime);
|
||||
markRootUpdated(root, lane, eventTime, updatePriority);
|
||||
|
||||
if (
|
||||
(executionContext & RenderContext) !== NoLanes &&
|
||||
|
|
@ -820,7 +855,12 @@ export function scheduleInitialHydrationOnRoot(
|
|||
// match what was rendered on the server.
|
||||
const current = root.current;
|
||||
current.lanes = lane;
|
||||
markRootUpdated(root, lane, eventTime);
|
||||
markRootUpdated(
|
||||
root,
|
||||
lane,
|
||||
eventTime,
|
||||
root.tag === LegacyRoot ? DiscreteEventPriority : DefaultEventPriority,
|
||||
);
|
||||
ensureRootIsScheduled(root, eventTime);
|
||||
}
|
||||
|
||||
|
|
@ -860,12 +900,15 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
|
|||
cancelCallback(existingCallbackNode);
|
||||
}
|
||||
root.callbackNode = null;
|
||||
root.callbackPriority = NoLane;
|
||||
root.callbackPriority = NoEventPriority;
|
||||
return;
|
||||
}
|
||||
|
||||
// We use the highest priority lane to represent the priority of the callback.
|
||||
const newCallbackPriority = getHighestPriorityLane(nextLanes);
|
||||
const newCallbackPriority = laneToEventPriority(
|
||||
getHighestPriorityLane(nextLanes),
|
||||
root.updatePriority,
|
||||
);
|
||||
|
||||
// Check if there's an existing task. We may be able to reuse it.
|
||||
const existingCallbackPriority = root.callbackPriority;
|
||||
|
|
@ -886,25 +929,42 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
|
|||
// TODO: Temporary until we confirm this warning is not fired.
|
||||
if (
|
||||
existingCallbackNode == null &&
|
||||
existingCallbackPriority !== SyncLane
|
||||
existingCallbackPriority !== DiscreteEventPriority
|
||||
) {
|
||||
console.error(
|
||||
'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',
|
||||
);
|
||||
}
|
||||
}
|
||||
// The priority hasn't changed. We can reuse the existing task. Exit.
|
||||
return;
|
||||
|
||||
if (
|
||||
enableFrameEndScheduling &&
|
||||
supportsFrameAlignedTask &&
|
||||
newCallbackPriority === DefaultEventPriority
|
||||
) {
|
||||
// Do nothing, we need to schedule a new rAF.
|
||||
} else {
|
||||
// The priority hasn't changed. We can reuse the existing task. Exit.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (existingCallbackNode != null) {
|
||||
if (existingCallbackNode !== null) {
|
||||
// Cancel the existing callback. We'll schedule a new one below.
|
||||
cancelCallback(existingCallbackNode);
|
||||
if (
|
||||
enableFrameEndScheduling &&
|
||||
supportsFrameAlignedTask &&
|
||||
existingCallbackPriority === DefaultEventPriority
|
||||
) {
|
||||
cancelFrameAlignedTask(existingCallbackNode);
|
||||
} else {
|
||||
cancelCallback(existingCallbackNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule a new callback.
|
||||
let newCallbackNode;
|
||||
if (newCallbackPriority === SyncLane) {
|
||||
if (newCallbackPriority === DiscreteEventPriority) {
|
||||
// Special case: Sync React callbacks are scheduled on a special
|
||||
// internal queue
|
||||
if (root.tag === LegacyRoot) {
|
||||
|
|
@ -943,9 +1003,26 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
|
|||
scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);
|
||||
}
|
||||
newCallbackNode = null;
|
||||
} else if (
|
||||
enableFrameEndScheduling &&
|
||||
supportsFrameAlignedTask &&
|
||||
newCallbackPriority === DefaultEventPriority
|
||||
) {
|
||||
if (__DEV__ && ReactCurrentActQueue.current !== null) {
|
||||
// Inside `act`, use our internal `act` queue so that these get flushed
|
||||
// at the end of the current scope even when using the sync version
|
||||
// of `act`.
|
||||
ReactCurrentActQueue.current.push(
|
||||
performConcurrentWorkOnRoot.bind(null, root),
|
||||
);
|
||||
} else {
|
||||
newCallbackNode = scheduleFrameAlignedTask(
|
||||
performConcurrentWorkOnRoot.bind(null, root),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let schedulerPriorityLevel;
|
||||
switch (lanesToEventPriority(nextLanes)) {
|
||||
switch (lanesToEventPriority(nextLanes, root.updatePriority)) {
|
||||
case DiscreteEventPriority:
|
||||
schedulerPriorityLevel = ImmediateSchedulerPriority;
|
||||
break;
|
||||
|
|
@ -1883,7 +1960,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
|
|||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logRenderStarted(lanes);
|
||||
logRenderStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1984,7 +2061,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
|
|||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logRenderStarted(lanes);
|
||||
logRenderStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2339,7 +2416,7 @@ function commitRootImpl(
|
|||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStarted(lanes);
|
||||
logCommitStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2382,7 +2459,7 @@ function commitRootImpl(
|
|||
// commitRoot never returns a continuation; it always finishes synchronously.
|
||||
// So we can clear these now to allow a new callback to be scheduled.
|
||||
root.callbackNode = null;
|
||||
root.callbackPriority = NoLane;
|
||||
root.callbackPriority = NoEventPriority;
|
||||
|
||||
// Check which lanes no longer have any work scheduled on them, and mark
|
||||
// those as finished.
|
||||
|
|
@ -2435,6 +2512,7 @@ function commitRootImpl(
|
|||
}
|
||||
}
|
||||
|
||||
const prevRootUpdatePriority = root.updatePriority;
|
||||
// Check if there are any effects in the whole tree.
|
||||
// TODO: This is left over from the effect list implementation, where we had
|
||||
// to check for the existence of `firstEffect` to satisfy Flow. I think the
|
||||
|
|
@ -2506,7 +2584,7 @@ function commitRootImpl(
|
|||
// layout, but class component lifecycles also fire here for legacy reasons.
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logLayoutEffectsStarted(lanes);
|
||||
logLayoutEffectsStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
if (enableSchedulingProfiler) {
|
||||
|
|
@ -2635,8 +2713,11 @@ function commitRootImpl(
|
|||
// TODO: We can optimize this by not scheduling the callback earlier. Since we
|
||||
// currently schedule the callback in multiple places, will wait until those
|
||||
// are consolidated.
|
||||
//// TODO: Need to clear the updatePriority inorder to remove the sync lane check
|
||||
if (
|
||||
includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&
|
||||
(enableUnifiedSyncLane
|
||||
? prevRootUpdatePriority === DiscreteEventPriority
|
||||
: includesSomeLane(pendingPassiveEffectsLanes, SyncLane)) &&
|
||||
root.tag !== LegacyRoot
|
||||
) {
|
||||
flushPassiveEffects();
|
||||
|
|
@ -2644,7 +2725,7 @@ function commitRootImpl(
|
|||
|
||||
// Read this again, since a passive effect might have updated it
|
||||
remainingLanes = root.pendingLanes;
|
||||
if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {
|
||||
if (includesSomeLane(remainingLanes, SyncLane)) {
|
||||
if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
|
||||
markNestedUpdateScheduled();
|
||||
}
|
||||
|
|
@ -2659,6 +2740,7 @@ function commitRootImpl(
|
|||
}
|
||||
} else {
|
||||
nestedUpdateCount = 0;
|
||||
root.updatePriority = NoEventPriority;
|
||||
}
|
||||
|
||||
// If layout work was scheduled, flush it now.
|
||||
|
|
@ -2766,7 +2848,10 @@ export function flushPassiveEffects(): boolean {
|
|||
const remainingLanes = pendingPassiveEffectsRemainingLanes;
|
||||
pendingPassiveEffectsRemainingLanes = NoLanes;
|
||||
|
||||
const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);
|
||||
const renderPriority = lanesToEventPriority(
|
||||
pendingPassiveEffectsLanes,
|
||||
root.updatePriority,
|
||||
);
|
||||
const priority = lowerEventPriority(DefaultEventPriority, renderPriority);
|
||||
const prevTransition = ReactCurrentBatchConfig.transition;
|
||||
const previousPriority = getCurrentUpdatePriority();
|
||||
|
|
@ -2827,7 +2912,7 @@ function flushPassiveEffectsImpl() {
|
|||
didScheduleUpdateDuringPassiveEffects = false;
|
||||
|
||||
if (enableDebugTracing) {
|
||||
logPassiveEffectsStarted(lanes);
|
||||
logPassiveEffectsStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2951,7 +3036,7 @@ function captureCommitPhaseErrorOnRoot(
|
|||
const root = enqueueUpdate(rootFiber, update, (SyncLane: Lane));
|
||||
const eventTime = requestEventTime();
|
||||
if (root !== null) {
|
||||
markRootUpdated(root, SyncLane, eventTime);
|
||||
markRootUpdated(root, SyncLane, eventTime, DiscreteEventPriority);
|
||||
ensureRootIsScheduled(root, eventTime);
|
||||
}
|
||||
}
|
||||
|
|
@ -3000,7 +3085,7 @@ export function captureCommitPhaseError(
|
|||
const root = enqueueUpdate(fiber, update, (SyncLane: Lane));
|
||||
const eventTime = requestEventTime();
|
||||
if (root !== null) {
|
||||
markRootUpdated(root, SyncLane, eventTime);
|
||||
markRootUpdated(root, SyncLane, eventTime, DiscreteEventPriority);
|
||||
ensureRootIsScheduled(root, eventTime);
|
||||
}
|
||||
return;
|
||||
|
|
@ -3136,7 +3221,8 @@ function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
|
|||
const eventTime = requestEventTime();
|
||||
const root = enqueueConcurrentRenderForLane(boundaryFiber, retryLane);
|
||||
if (root !== null) {
|
||||
markRootUpdated(root, retryLane, eventTime);
|
||||
// console.log(retryLane,root.updatePriority, currentUpdatePriority, new Error().stack);
|
||||
markRootUpdated(root, retryLane, eventTime, currentUpdatePriority);
|
||||
ensureRootIsScheduled(root, eventTime);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ import {
|
|||
enableUpdaterTracking,
|
||||
enableCache,
|
||||
enableTransitionTracing,
|
||||
enableFrameEndScheduling,
|
||||
enableUnifiedSyncLane,
|
||||
useModernStrictMode,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
|
|
@ -86,8 +88,10 @@ import {
|
|||
scheduleMicrotask,
|
||||
prepareRendererToRender,
|
||||
resetRendererAfterRender,
|
||||
cancelFrameAlignedTask,
|
||||
scheduleFrameAlignedTask,
|
||||
supportsFrameAlignedTask,
|
||||
} from './ReactFiberHostConfig';
|
||||
|
||||
import {
|
||||
createWorkInProgress,
|
||||
assignFiberPropertiesInDEV,
|
||||
|
|
@ -163,16 +167,20 @@ import {
|
|||
movePendingFibersToMemoized,
|
||||
addTransitionToLanesMap,
|
||||
getTransitionsForLanes,
|
||||
InputContinuousLane,
|
||||
DefaultLane,
|
||||
} from './ReactFiberLane.old';
|
||||
import {
|
||||
DiscreteEventPriority,
|
||||
ContinuousEventPriority,
|
||||
DefaultEventPriority,
|
||||
NoEventPriority,
|
||||
IdleEventPriority,
|
||||
getCurrentUpdatePriority,
|
||||
setCurrentUpdatePriority,
|
||||
lowerEventPriority,
|
||||
lanesToEventPriority,
|
||||
laneToEventPriority,
|
||||
} from './ReactEventPriorities.old';
|
||||
import {requestCurrentTransition, NoTransition} from './ReactFiberTransition';
|
||||
import {beginWork as originalBeginWork} from './ReactFiberBeginWork.old';
|
||||
|
|
@ -599,10 +607,17 @@ export function getCurrentTime(): number {
|
|||
return now();
|
||||
}
|
||||
|
||||
let currentUpdatePriority = NoEventPriority;
|
||||
|
||||
export function requestUpdateLane_getUpdatePriority(): EventPriority {
|
||||
return currentUpdatePriority;
|
||||
}
|
||||
|
||||
export function requestUpdateLane(fiber: Fiber): Lane {
|
||||
// Special cases
|
||||
const mode = fiber.mode;
|
||||
if ((mode & ConcurrentMode) === NoMode) {
|
||||
currentUpdatePriority = DiscreteEventPriority;
|
||||
return (SyncLane: Lane);
|
||||
} else if (
|
||||
!deferRenderPhaseUpdateToNextBatch &&
|
||||
|
|
@ -618,7 +633,11 @@ export function requestUpdateLane(fiber: Fiber): Lane {
|
|||
// This behavior is only a fallback. The flag only exists until we can roll
|
||||
// out the setState warning, since existing code might accidentally rely on
|
||||
// the current behavior.
|
||||
return pickArbitraryLane(workInProgressRootRenderLanes);
|
||||
const nextLane = pickArbitraryLane(workInProgressRootRenderLanes);
|
||||
if ((nextLane & SyncLane) === NoLane) {
|
||||
currentUpdatePriority = NoEventPriority;
|
||||
}
|
||||
return nextLane;
|
||||
}
|
||||
|
||||
const isTransition = requestCurrentTransition() !== NoTransition;
|
||||
|
|
@ -642,28 +661,42 @@ export function requestUpdateLane(fiber: Fiber): Lane {
|
|||
// All transitions within the same event are assigned the same lane.
|
||||
currentEventTransitionLane = claimNextTransitionLane();
|
||||
}
|
||||
if ((currentEventTransitionLane & SyncLane) === NoLane) {
|
||||
currentUpdatePriority = NoEventPriority;
|
||||
}
|
||||
|
||||
return currentEventTransitionLane;
|
||||
}
|
||||
|
||||
// Updates originating inside certain React methods, like flushSync, have
|
||||
// their priority set by tracking it with a context variable.
|
||||
//
|
||||
// The opaque type returned by the host config is internally a lane, so we can
|
||||
// use that directly.
|
||||
// TODO: Move this type conversion to the event priority module.
|
||||
const updateLane: Lane = (getCurrentUpdatePriority(): any);
|
||||
if (updateLane !== NoLane) {
|
||||
return updateLane;
|
||||
const updatePriority = getCurrentUpdatePriority();
|
||||
currentUpdatePriority = updatePriority;
|
||||
if (updatePriority !== NoEventPriority) {
|
||||
if (updatePriority === DefaultEventPriority) {
|
||||
return enableUnifiedSyncLane ? SyncLane : DefaultLane;
|
||||
}
|
||||
if (updatePriority === ContinuousEventPriority) {
|
||||
return InputContinuousLane;
|
||||
}
|
||||
return (updatePriority: any);
|
||||
}
|
||||
|
||||
// This update originated outside React. Ask the host environment for an
|
||||
// appropriate priority, based on the type of event.
|
||||
//
|
||||
// The opaque type returned by the host config is internally a lane, so we can
|
||||
// use that directly.
|
||||
// TODO: Move this type conversion to the event priority module.
|
||||
const eventLane: Lane = (getCurrentEventPriority(): any);
|
||||
return eventLane;
|
||||
const eventPriority = getCurrentEventPriority();
|
||||
currentUpdatePriority = eventPriority;
|
||||
if (eventPriority === DefaultEventPriority) {
|
||||
return enableUnifiedSyncLane ? SyncLane : DefaultLane;
|
||||
}
|
||||
if (eventPriority === ContinuousEventPriority) {
|
||||
return InputContinuousLane;
|
||||
}
|
||||
return (eventPriority: any);
|
||||
}
|
||||
|
||||
function requestRetryLane(fiber: Fiber) {
|
||||
|
|
@ -674,8 +707,10 @@ function requestRetryLane(fiber: Fiber) {
|
|||
// Special cases
|
||||
const mode = fiber.mode;
|
||||
if ((mode & ConcurrentMode) === NoMode) {
|
||||
currentUpdatePriority = DiscreteEventPriority;
|
||||
return (SyncLane: Lane);
|
||||
}
|
||||
currentUpdatePriority = DefaultEventPriority;
|
||||
|
||||
return claimNextRetryLane();
|
||||
}
|
||||
|
|
@ -685,6 +720,7 @@ export function scheduleUpdateOnFiber(
|
|||
fiber: Fiber,
|
||||
lane: Lane,
|
||||
eventTime: number,
|
||||
updatePriority: EventPriority,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (isRunningInsertionEffect) {
|
||||
|
|
@ -697,9 +733,8 @@ export function scheduleUpdateOnFiber(
|
|||
didScheduleUpdateDuringPassiveEffects = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark that the root has a pending update.
|
||||
markRootUpdated(root, lane, eventTime);
|
||||
markRootUpdated(root, lane, eventTime, updatePriority);
|
||||
|
||||
if (
|
||||
(executionContext & RenderContext) !== NoLanes &&
|
||||
|
|
@ -820,7 +855,12 @@ export function scheduleInitialHydrationOnRoot(
|
|||
// match what was rendered on the server.
|
||||
const current = root.current;
|
||||
current.lanes = lane;
|
||||
markRootUpdated(root, lane, eventTime);
|
||||
markRootUpdated(
|
||||
root,
|
||||
lane,
|
||||
eventTime,
|
||||
root.tag === LegacyRoot ? DiscreteEventPriority : DefaultEventPriority,
|
||||
);
|
||||
ensureRootIsScheduled(root, eventTime);
|
||||
}
|
||||
|
||||
|
|
@ -860,12 +900,15 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
|
|||
cancelCallback(existingCallbackNode);
|
||||
}
|
||||
root.callbackNode = null;
|
||||
root.callbackPriority = NoLane;
|
||||
root.callbackPriority = NoEventPriority;
|
||||
return;
|
||||
}
|
||||
|
||||
// We use the highest priority lane to represent the priority of the callback.
|
||||
const newCallbackPriority = getHighestPriorityLane(nextLanes);
|
||||
const newCallbackPriority = laneToEventPriority(
|
||||
getHighestPriorityLane(nextLanes),
|
||||
root.updatePriority,
|
||||
);
|
||||
|
||||
// Check if there's an existing task. We may be able to reuse it.
|
||||
const existingCallbackPriority = root.callbackPriority;
|
||||
|
|
@ -886,25 +929,42 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
|
|||
// TODO: Temporary until we confirm this warning is not fired.
|
||||
if (
|
||||
existingCallbackNode == null &&
|
||||
existingCallbackPriority !== SyncLane
|
||||
existingCallbackPriority !== DiscreteEventPriority
|
||||
) {
|
||||
console.error(
|
||||
'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',
|
||||
);
|
||||
}
|
||||
}
|
||||
// The priority hasn't changed. We can reuse the existing task. Exit.
|
||||
return;
|
||||
|
||||
if (
|
||||
enableFrameEndScheduling &&
|
||||
supportsFrameAlignedTask &&
|
||||
newCallbackPriority === DefaultEventPriority
|
||||
) {
|
||||
// Do nothing, we need to schedule a new rAF.
|
||||
} else {
|
||||
// The priority hasn't changed. We can reuse the existing task. Exit.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (existingCallbackNode != null) {
|
||||
if (existingCallbackNode !== null) {
|
||||
// Cancel the existing callback. We'll schedule a new one below.
|
||||
cancelCallback(existingCallbackNode);
|
||||
if (
|
||||
enableFrameEndScheduling &&
|
||||
supportsFrameAlignedTask &&
|
||||
existingCallbackPriority === DefaultEventPriority
|
||||
) {
|
||||
cancelFrameAlignedTask(existingCallbackNode);
|
||||
} else {
|
||||
cancelCallback(existingCallbackNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule a new callback.
|
||||
let newCallbackNode;
|
||||
if (newCallbackPriority === SyncLane) {
|
||||
if (newCallbackPriority === DiscreteEventPriority) {
|
||||
// Special case: Sync React callbacks are scheduled on a special
|
||||
// internal queue
|
||||
if (root.tag === LegacyRoot) {
|
||||
|
|
@ -943,9 +1003,26 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
|
|||
scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);
|
||||
}
|
||||
newCallbackNode = null;
|
||||
} else if (
|
||||
enableFrameEndScheduling &&
|
||||
supportsFrameAlignedTask &&
|
||||
newCallbackPriority === DefaultEventPriority
|
||||
) {
|
||||
if (__DEV__ && ReactCurrentActQueue.current !== null) {
|
||||
// Inside `act`, use our internal `act` queue so that these get flushed
|
||||
// at the end of the current scope even when using the sync version
|
||||
// of `act`.
|
||||
ReactCurrentActQueue.current.push(
|
||||
performConcurrentWorkOnRoot.bind(null, root),
|
||||
);
|
||||
} else {
|
||||
newCallbackNode = scheduleFrameAlignedTask(
|
||||
performConcurrentWorkOnRoot.bind(null, root),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let schedulerPriorityLevel;
|
||||
switch (lanesToEventPriority(nextLanes)) {
|
||||
switch (lanesToEventPriority(nextLanes, root.updatePriority)) {
|
||||
case DiscreteEventPriority:
|
||||
schedulerPriorityLevel = ImmediateSchedulerPriority;
|
||||
break;
|
||||
|
|
@ -1883,7 +1960,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
|
|||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logRenderStarted(lanes);
|
||||
logRenderStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1984,7 +2061,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
|
|||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logRenderStarted(lanes);
|
||||
logRenderStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2339,7 +2416,7 @@ function commitRootImpl(
|
|||
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logCommitStarted(lanes);
|
||||
logCommitStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2382,7 +2459,7 @@ function commitRootImpl(
|
|||
// commitRoot never returns a continuation; it always finishes synchronously.
|
||||
// So we can clear these now to allow a new callback to be scheduled.
|
||||
root.callbackNode = null;
|
||||
root.callbackPriority = NoLane;
|
||||
root.callbackPriority = NoEventPriority;
|
||||
|
||||
// Check which lanes no longer have any work scheduled on them, and mark
|
||||
// those as finished.
|
||||
|
|
@ -2435,6 +2512,7 @@ function commitRootImpl(
|
|||
}
|
||||
}
|
||||
|
||||
const prevRootUpdatePriority = root.updatePriority;
|
||||
// Check if there are any effects in the whole tree.
|
||||
// TODO: This is left over from the effect list implementation, where we had
|
||||
// to check for the existence of `firstEffect` to satisfy Flow. I think the
|
||||
|
|
@ -2506,7 +2584,7 @@ function commitRootImpl(
|
|||
// layout, but class component lifecycles also fire here for legacy reasons.
|
||||
if (__DEV__) {
|
||||
if (enableDebugTracing) {
|
||||
logLayoutEffectsStarted(lanes);
|
||||
logLayoutEffectsStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
if (enableSchedulingProfiler) {
|
||||
|
|
@ -2635,8 +2713,11 @@ function commitRootImpl(
|
|||
// TODO: We can optimize this by not scheduling the callback earlier. Since we
|
||||
// currently schedule the callback in multiple places, will wait until those
|
||||
// are consolidated.
|
||||
//// TODO: Need to clear the updatePriority inorder to remove the sync lane check
|
||||
if (
|
||||
includesSomeLane(pendingPassiveEffectsLanes, SyncLane) &&
|
||||
(enableUnifiedSyncLane
|
||||
? prevRootUpdatePriority === DiscreteEventPriority
|
||||
: includesSomeLane(pendingPassiveEffectsLanes, SyncLane)) &&
|
||||
root.tag !== LegacyRoot
|
||||
) {
|
||||
flushPassiveEffects();
|
||||
|
|
@ -2644,7 +2725,7 @@ function commitRootImpl(
|
|||
|
||||
// Read this again, since a passive effect might have updated it
|
||||
remainingLanes = root.pendingLanes;
|
||||
if (includesSomeLane(remainingLanes, (SyncLane: Lane))) {
|
||||
if (includesSomeLane(remainingLanes, SyncLane)) {
|
||||
if (enableProfilerTimer && enableProfilerNestedUpdatePhase) {
|
||||
markNestedUpdateScheduled();
|
||||
}
|
||||
|
|
@ -2659,6 +2740,7 @@ function commitRootImpl(
|
|||
}
|
||||
} else {
|
||||
nestedUpdateCount = 0;
|
||||
root.updatePriority = NoEventPriority;
|
||||
}
|
||||
|
||||
// If layout work was scheduled, flush it now.
|
||||
|
|
@ -2766,7 +2848,10 @@ export function flushPassiveEffects(): boolean {
|
|||
const remainingLanes = pendingPassiveEffectsRemainingLanes;
|
||||
pendingPassiveEffectsRemainingLanes = NoLanes;
|
||||
|
||||
const renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);
|
||||
const renderPriority = lanesToEventPriority(
|
||||
pendingPassiveEffectsLanes,
|
||||
root.updatePriority,
|
||||
);
|
||||
const priority = lowerEventPriority(DefaultEventPriority, renderPriority);
|
||||
const prevTransition = ReactCurrentBatchConfig.transition;
|
||||
const previousPriority = getCurrentUpdatePriority();
|
||||
|
|
@ -2827,7 +2912,7 @@ function flushPassiveEffectsImpl() {
|
|||
didScheduleUpdateDuringPassiveEffects = false;
|
||||
|
||||
if (enableDebugTracing) {
|
||||
logPassiveEffectsStarted(lanes);
|
||||
logPassiveEffectsStarted(lanes, root.updatePriority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2951,7 +3036,7 @@ function captureCommitPhaseErrorOnRoot(
|
|||
const root = enqueueUpdate(rootFiber, update, (SyncLane: Lane));
|
||||
const eventTime = requestEventTime();
|
||||
if (root !== null) {
|
||||
markRootUpdated(root, SyncLane, eventTime);
|
||||
markRootUpdated(root, SyncLane, eventTime, DiscreteEventPriority);
|
||||
ensureRootIsScheduled(root, eventTime);
|
||||
}
|
||||
}
|
||||
|
|
@ -3000,7 +3085,7 @@ export function captureCommitPhaseError(
|
|||
const root = enqueueUpdate(fiber, update, (SyncLane: Lane));
|
||||
const eventTime = requestEventTime();
|
||||
if (root !== null) {
|
||||
markRootUpdated(root, SyncLane, eventTime);
|
||||
markRootUpdated(root, SyncLane, eventTime, DiscreteEventPriority);
|
||||
ensureRootIsScheduled(root, eventTime);
|
||||
}
|
||||
return;
|
||||
|
|
@ -3136,7 +3221,8 @@ function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
|
|||
const eventTime = requestEventTime();
|
||||
const root = enqueueConcurrentRenderForLane(boundaryFiber, retryLane);
|
||||
if (root !== null) {
|
||||
markRootUpdated(root, retryLane, eventTime);
|
||||
// console.log(retryLane,root.updatePriority, currentUpdatePriority, new Error().stack);
|
||||
markRootUpdated(root, retryLane, eventTime, currentUpdatePriority);
|
||||
ensureRootIsScheduled(root, eventTime);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ import type {
|
|||
import type {WorkTag} from './ReactWorkTags';
|
||||
import type {TypeOfMode} from './ReactTypeOfMode';
|
||||
import type {Flags} from './ReactFiberFlags';
|
||||
import type {Lane, Lanes, LaneMap} from './ReactFiberLane.old';
|
||||
import type {Lanes, LaneMap} from './ReactFiberLane.old';
|
||||
import type {EventPriority} from './ReactEventPriorities';
|
||||
import type {RootTag} from './ReactRootTags';
|
||||
import type {
|
||||
Container,
|
||||
|
|
@ -239,7 +240,9 @@ type BaseFiberRootProperties = {
|
|||
// Node returned by Scheduler.scheduleCallback. Represents the next rendering
|
||||
// task that the root will work on.
|
||||
callbackNode: any,
|
||||
callbackPriority: Lane,
|
||||
callbackPriority: EventPriority,
|
||||
frameAlignedNode?: number | null,
|
||||
updatePriority: EventPriority,
|
||||
eventTimes: LaneMap<number>,
|
||||
expirationTimes: LaneMap<number>,
|
||||
hiddenUpdates: LaneMap<Array<ConcurrentUpdate> | null>,
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ describe('DebugTracing', () => {
|
|||
|
||||
let logs;
|
||||
|
||||
const DEFAULT_LANE_STRING = '0b0000000000000000000000000010000';
|
||||
const SYNC_LANE_STRING = '0b0000000000000000000000000000001';
|
||||
const RETRY_LANE_STRING = '0b0000000010000000000000000000000';
|
||||
const DEFAULT_EVENT_PRIORITY = 3;
|
||||
|
||||
global.IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
|
|
@ -87,9 +88,9 @@ describe('DebugTracing', () => {
|
|||
);
|
||||
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ render (0b0000000000000000000000000000001)',
|
||||
'group: ⚛️ render (0b0000000000000000000000000000001) (1)',
|
||||
'log: ⚛️ Example suspended',
|
||||
'groupEnd: ⚛️ render (0b0000000000000000000000000000001)',
|
||||
'groupEnd: ⚛️ render (0b0000000000000000000000000000001) (1)',
|
||||
]);
|
||||
|
||||
logs.splice(0);
|
||||
|
|
@ -121,9 +122,9 @@ describe('DebugTracing', () => {
|
|||
);
|
||||
|
||||
expect(logs).toEqual([
|
||||
'group: ⚛️ render (0b0000000000000000000000000000001)',
|
||||
'group: ⚛️ render (0b0000000000000000000000000000001) (1)',
|
||||
'log: <Wrapper/>',
|
||||
'groupEnd: ⚛️ render (0b0000000000000000000000000000001)',
|
||||
'groupEnd: ⚛️ render (0b0000000000000000000000000000001) (1)',
|
||||
]);
|
||||
|
||||
logs.splice(0);
|
||||
|
|
@ -131,13 +132,13 @@ describe('DebugTracing', () => {
|
|||
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||
|
||||
expect(logs).toEqual([
|
||||
`group: ⚛️ render (${RETRY_LANE_STRING})`,
|
||||
`group: ⚛️ render (${RETRY_LANE_STRING}) (0)`,
|
||||
'log: <Example/>',
|
||||
`groupEnd: ⚛️ render (${RETRY_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ render (${RETRY_LANE_STRING}) (0)`,
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && build === 'development' && enableDebugTracing
|
||||
// @gate experimental && build === 'development' && enableDebugTracing && enableUnifiedSyncLane
|
||||
it('should log concurrent render with suspense', async () => {
|
||||
let isResolved = false;
|
||||
let resolveFakeSuspensePromise;
|
||||
|
|
@ -167,9 +168,9 @@ describe('DebugTracing', () => {
|
|||
);
|
||||
|
||||
expect(logs).toEqual([
|
||||
`group: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
'log: ⚛️ Example suspended',
|
||||
`groupEnd: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
]);
|
||||
|
||||
logs.splice(0);
|
||||
|
|
@ -178,7 +179,7 @@ describe('DebugTracing', () => {
|
|||
expect(logs).toEqual(['log: ⚛️ Example resolved']);
|
||||
});
|
||||
|
||||
// @gate experimental && build === 'development' && enableDebugTracing && enableCPUSuspense
|
||||
// @gate experimental && build === 'development' && enableDebugTracing && enableCPUSuspense && enableUnifiedSyncLane
|
||||
it('should log concurrent render with CPU suspense', () => {
|
||||
function Example() {
|
||||
console.log('<Example/>');
|
||||
|
|
@ -204,16 +205,16 @@ describe('DebugTracing', () => {
|
|||
);
|
||||
|
||||
expect(logs).toEqual([
|
||||
`group: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
'log: <Wrapper/>',
|
||||
`groupEnd: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ render (${RETRY_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
`group: ⚛️ render (${RETRY_LANE_STRING}) (0)`,
|
||||
'log: <Example/>',
|
||||
`groupEnd: ⚛️ render (${RETRY_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ render (${RETRY_LANE_STRING}) (0)`,
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && build === 'development' && enableDebugTracing
|
||||
// @gate experimental && build === 'development' && enableDebugTracing && enableUnifiedSyncLane
|
||||
it('should log cascading class component updates', () => {
|
||||
class Example extends React.Component {
|
||||
state = {didMount: false};
|
||||
|
|
@ -235,15 +236,15 @@ describe('DebugTracing', () => {
|
|||
);
|
||||
|
||||
expect(logs).toEqual([
|
||||
`group: ⚛️ commit (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ layout effects (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ commit (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
`group: ⚛️ layout effects (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
'log: ⚛️ Example updated state (0b0000000000000000000000000000001)',
|
||||
`groupEnd: ⚛️ layout effects (${DEFAULT_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ commit (${DEFAULT_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ layout effects (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
`groupEnd: ⚛️ commit (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && build === 'development' && enableDebugTracing
|
||||
// @gate experimental && build === 'development' && enableDebugTracing && enableUnifiedSyncLane
|
||||
it('should log render phase state updates for class component', () => {
|
||||
class Example extends React.Component {
|
||||
state = {didRender: false};
|
||||
|
|
@ -267,13 +268,13 @@ describe('DebugTracing', () => {
|
|||
}).toErrorDev('Cannot update during an existing state transition');
|
||||
|
||||
expect(logs).toEqual([
|
||||
`group: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`log: ⚛️ Example updated state (${DEFAULT_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
`log: ⚛️ Example updated state (${SYNC_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && build === 'development' && enableDebugTracing
|
||||
// @gate experimental && build === 'development' && enableDebugTracing && enableUnifiedSyncLane
|
||||
it('should log cascading layout updates', () => {
|
||||
function Example() {
|
||||
const [didMount, setDidMount] = React.useState(false);
|
||||
|
|
@ -293,15 +294,15 @@ describe('DebugTracing', () => {
|
|||
);
|
||||
|
||||
expect(logs).toEqual([
|
||||
`group: ⚛️ commit (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ layout effects (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ commit (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
`group: ⚛️ layout effects (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
'log: ⚛️ Example updated state (0b0000000000000000000000000000001)',
|
||||
`groupEnd: ⚛️ layout effects (${DEFAULT_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ commit (${DEFAULT_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ layout effects (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
`groupEnd: ⚛️ commit (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && build === 'development' && enableDebugTracing
|
||||
// @gate experimental && build === 'development' && enableDebugTracing && enableUnifiedSyncLane
|
||||
it('should log cascading passive updates', () => {
|
||||
function Example() {
|
||||
const [didMount, setDidMount] = React.useState(false);
|
||||
|
|
@ -320,13 +321,14 @@ describe('DebugTracing', () => {
|
|||
);
|
||||
});
|
||||
expect(logs).toEqual([
|
||||
`group: ⚛️ passive effects (${DEFAULT_LANE_STRING})`,
|
||||
`log: ⚛️ Example updated state (${DEFAULT_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ passive effects (${DEFAULT_LANE_STRING})`,
|
||||
// TODO: why does this become 0?
|
||||
`group: ⚛️ passive effects (${SYNC_LANE_STRING}) (0)`,
|
||||
`log: ⚛️ Example updated state (${SYNC_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ passive effects (${SYNC_LANE_STRING}) (0)`,
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && build === 'development' && enableDebugTracing
|
||||
// @gate experimental && build === 'development' && enableDebugTracing && enableUnifiedSyncLane
|
||||
it('should log render phase updates', () => {
|
||||
function Example() {
|
||||
const [didRender, setDidRender] = React.useState(false);
|
||||
|
|
@ -346,13 +348,13 @@ describe('DebugTracing', () => {
|
|||
});
|
||||
|
||||
expect(logs).toEqual([
|
||||
`group: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`log: ⚛️ Example updated state (${DEFAULT_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
`log: ⚛️ Example updated state (${SYNC_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate experimental && build === 'development' && enableDebugTracing
|
||||
// @gate experimental && build === 'development' && enableDebugTracing && enableUnifiedSyncLane
|
||||
it('should log when user code logs', () => {
|
||||
function Example() {
|
||||
console.log('Hello from user code');
|
||||
|
|
@ -369,9 +371,9 @@ describe('DebugTracing', () => {
|
|||
);
|
||||
|
||||
expect(logs).toEqual([
|
||||
`group: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`group: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
'log: Hello from user code',
|
||||
`groupEnd: ⚛️ render (${DEFAULT_LANE_STRING})`,
|
||||
`groupEnd: ⚛️ render (${SYNC_LANE_STRING}) (${DEFAULT_EVENT_PRIORITY})`,
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -157,12 +157,17 @@ describe('ReactBlockingMode', () => {
|
|||
}),
|
||||
);
|
||||
|
||||
// Only the second update should have flushed synchronously
|
||||
expect(Scheduler).toHaveYielded(['B1']);
|
||||
expect(root).toMatchRenderedOutput('A0B1');
|
||||
if (gate(flags => flags.enableUnifiedSyncLane)) {
|
||||
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');
|
||||
// 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.enableUnifiedSyncLane)) {
|
||||
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]',
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ describe('ReactFiberHostContext', () => {
|
|||
prepareRendererToRender: function() {},
|
||||
resetRendererAfterRender: function() {},
|
||||
supportsMutation: true,
|
||||
supportsFrameAlignedTask: false,
|
||||
requestPostPaintCallback: function() {},
|
||||
});
|
||||
|
||||
|
|
@ -136,6 +137,7 @@ describe('ReactFiberHostContext', () => {
|
|||
prepareRendererToRender: function() {},
|
||||
resetRendererAfterRender: function() {},
|
||||
supportsMutation: true,
|
||||
shouldScheduleAnimationFrame: () => false,
|
||||
});
|
||||
|
||||
const container = Renderer.createContainer(
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ describe('ReactFlushSync', () => {
|
|||
ReactNoop.flushSync(() => setSyncState(1));
|
||||
}
|
||||
}, [syncState, state]);
|
||||
return <Text text={`${syncState}, ${state}`} />;
|
||||
return <Text text={`${syncState} / ${state}`} />;
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
|
|
@ -49,22 +49,28 @@ describe('ReactFlushSync', () => {
|
|||
root.render(<App />);
|
||||
}
|
||||
// This will yield right before the passive effect fires
|
||||
expect(Scheduler).toFlushUntilNextPaint(['0, 0']);
|
||||
expect(Scheduler).toFlushUntilNextPaint(['0 / 0']);
|
||||
|
||||
// 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.enableUnifiedSyncLane) ? ['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.enableUnifiedSyncLane)) {
|
||||
expect(Scheduler).toFlushUntilNextPaint([]);
|
||||
} else {
|
||||
// Now flush it.
|
||||
expect(Scheduler).toFlushUntilNextPaint(['1 / 1']);
|
||||
}
|
||||
});
|
||||
expect(root).toMatchRenderedOutput('1, 1');
|
||||
expect(root).toMatchRenderedOutput('1 / 1');
|
||||
});
|
||||
|
||||
test('nested with startTransition', async () => {
|
||||
|
|
|
|||
|
|
@ -568,8 +568,13 @@ describe('ReactHooks', () => {
|
|||
});
|
||||
};
|
||||
|
||||
// Update at normal priority
|
||||
ReactTestRenderer.unstable_batchedUpdates(() => update(n => n * 100));
|
||||
if (gate(flags => flags.enableUnifiedSyncLane)) {
|
||||
// Update at low 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.enableUnifiedSyncLane)) {
|
||||
React.startTransition(() => {
|
||||
setRow(20);
|
||||
});
|
||||
} else {
|
||||
setRow(20);
|
||||
}
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Up', 'Down']);
|
||||
expect(root).toMatchRenderedOutput(<span prop="Down" />);
|
||||
|
|
@ -955,11 +961,16 @@ describe('ReactHooksWithNoopRenderer', () => {
|
|||
ReactNoop.flushSync(() => {
|
||||
counter.current.dispatch(INCREMENT);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Count: 1']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
|
||||
if (gate(flags => flags.enableUnifiedSyncLane)) {
|
||||
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')]);
|
||||
expect(Scheduler).toFlushAndYield(['Count: 4']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 4')]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1717,11 +1728,16 @@ 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.enableUnifiedSyncLane)) {
|
||||
// updateCounts are batched
|
||||
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')]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1878,28 +1878,24 @@ describe('ReactIncremental', () => {
|
|||
'ShowLocale {"locale":"de"}',
|
||||
'ShowBoth {"locale":"de"}',
|
||||
]);
|
||||
|
||||
ReactNoop.render(
|
||||
<Intl locale="sv">
|
||||
<ShowLocale />
|
||||
<div>
|
||||
<ShowBoth />
|
||||
</div>
|
||||
</Intl>,
|
||||
);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
React.startTransition(() => {
|
||||
ReactNoop.render(
|
||||
<Intl locale="sv">
|
||||
<ShowLocale />
|
||||
<div>
|
||||
<ShowBoth />
|
||||
</div>
|
||||
</Intl>,
|
||||
);
|
||||
});
|
||||
expect(Scheduler).toFlushAndYieldThrough([
|
||||
'Intl {}',
|
||||
'ShowLocale {"locale":"sv"}',
|
||||
'ShowBoth {"locale":"sv"}',
|
||||
]);
|
||||
} else {
|
||||
ReactNoop.render(
|
||||
<Intl locale="sv">
|
||||
<ShowLocale />
|
||||
<div>
|
||||
<ShowBoth />
|
||||
</div>
|
||||
</Intl>,
|
||||
);
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Intl {}']);
|
||||
}
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Intl {}']);
|
||||
|
||||
ReactNoop.render(
|
||||
<Intl locale="en">
|
||||
|
|
@ -1910,21 +1906,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', () => {
|
||||
|
|
@ -2746,19 +2758,22 @@ describe('ReactIncremental', () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
ReactNoop.render(<Parent step={1} />);
|
||||
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
React.startTransition(() => {
|
||||
ReactNoop.render(<Parent step={1} />);
|
||||
});
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Parent: 1', 'Child: 1']);
|
||||
} else {
|
||||
ReactNoop.render(<Parent step={1} />);
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Parent: 1']);
|
||||
}
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Parent: 1']);
|
||||
|
||||
// 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', () => {
|
||||
|
|
@ -2772,20 +2787,22 @@ describe('ReactIncremental', () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
ReactNoop.render(<Parent step={1} />);
|
||||
if (gate(flags => flags.enableSyncDefaultUpdates)) {
|
||||
React.startTransition(() => {
|
||||
ReactNoop.render(<Parent step={1} />);
|
||||
});
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Parent: 1', 'Child: 1']);
|
||||
} else {
|
||||
ReactNoop.render(<Parent step={1} />);
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Parent: 1']);
|
||||
}
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Parent: 1']);
|
||||
|
||||
|
||||
// Interrupt at lower priority
|
||||
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,45 @@ describe('ReactIncrementalUpdates', () => {
|
|||
});
|
||||
|
||||
// The sync updates should have flushed, but not the async ones
|
||||
expect(Scheduler).toHaveYielded(['e', 'f']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('ef')]);
|
||||
if (gate(flags => flags.enableUnifiedSyncLane)) {
|
||||
expect(Scheduler).toHaveYielded(['d', 'e', 'f']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('def')]);
|
||||
} else {
|
||||
expect(Scheduler).toHaveYielded(['e', 'f']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('ef')]);
|
||||
}
|
||||
|
||||
// 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.enableUnifiedSyncLane)) {
|
||||
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 +310,44 @@ describe('ReactIncrementalUpdates', () => {
|
|||
});
|
||||
|
||||
// The sync updates should have flushed, but not the async ones.
|
||||
expect(Scheduler).toHaveYielded(['e', 'f']);
|
||||
if (gate(flags => flags.enableUnifiedSyncLane)) {
|
||||
expect(Scheduler).toHaveYielded(['d', 'e', 'f']);
|
||||
} else {
|
||||
expect(Scheduler).toHaveYielded(['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.enableUnifiedSyncLane)) {
|
||||
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'));
|
||||
|
|
|
|||
|
|
@ -569,8 +569,15 @@ describe('ReactOffscreen', () => {
|
|||
);
|
||||
|
||||
// Before the inner update can finish, we receive another pair of updates.
|
||||
setOuter(2);
|
||||
setInner(2);
|
||||
if (gate(flags => flags.enableUnifiedSyncLane)) {
|
||||
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);
|
||||
startTransition(() => {
|
||||
setStep(1);
|
||||
});
|
||||
ReactNoop.flushSync(() => {
|
||||
setText('B');
|
||||
});
|
||||
|
|
@ -523,8 +525,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,31 @@ describe('ReactTransition', () => {
|
|||
updateNormalPri();
|
||||
});
|
||||
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// Finish transition update.
|
||||
'Normal pri: 0',
|
||||
'Commit',
|
||||
|
||||
// Normal pri update.
|
||||
'Transition pri: 1',
|
||||
'Normal pri: 1',
|
||||
'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',
|
||||
]);
|
||||
} 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');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -186,6 +186,10 @@ describe('updaters', () => {
|
|||
let triggerActiveCascade = null;
|
||||
let triggerPassiveCascade = null;
|
||||
|
||||
// TODO: What should we do in tests like this,
|
||||
// where we're simulating default events?
|
||||
window.event = 'test';
|
||||
|
||||
const Parent = () => <SchedulingComponent />;
|
||||
const SchedulingComponent = () => {
|
||||
const [cascade, setCascade] = React.useState(null);
|
||||
|
|
@ -367,7 +371,13 @@ describe('updaters', () => {
|
|||
onCommitRootShouldYield = true;
|
||||
|
||||
await act(async () => {
|
||||
triggerError();
|
||||
if (gate(flags => flags.enableUnifiedSyncLane)) {
|
||||
React.startTransition(() => {
|
||||
triggerError();
|
||||
});
|
||||
} else {
|
||||
triggerError();
|
||||
}
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['onCommitRoot', 'error', 'onCommitRoot']);
|
||||
expect(allSchedulerTypes).toEqual([[Parent], [ErrorBoundary]]);
|
||||
|
|
|
|||
|
|
@ -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.enableUnifiedSyncLane)) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -78,6 +78,14 @@ export const resetRendererAfterRender = $$$hostConfig.resetRendererAfterRender;
|
|||
export const supportsMicrotasks = $$$hostConfig.supportsMicrotasks;
|
||||
export const scheduleMicrotask = $$$hostConfig.scheduleMicrotask;
|
||||
|
||||
// -------------------
|
||||
// Animation Frame
|
||||
// (optional)
|
||||
// -------------------
|
||||
export const supportsFrameAlignedTask = $$$hostConfig.supportsFrameAlignedTask;
|
||||
export const scheduleFrameAlignedTask = $$$hostConfig.scheduleFrameAlignedTask;
|
||||
export const cancelFrameAlignedTask = $$$hostConfig.cancelFrameAlignedTask;
|
||||
|
||||
// -------------------
|
||||
// Test selectors
|
||||
// (optional)
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ export const enableLegacyFBSupport = false;
|
|||
export const enableCache = __EXPERIMENTAL__;
|
||||
export const enableCacheElement = __EXPERIMENTAL__;
|
||||
|
||||
export const enableFrameEndScheduling = __EXPERIMENTAL__;
|
||||
|
||||
export const enableTransitionTracing = false;
|
||||
|
||||
// No known bugs, but needs performance testing
|
||||
|
|
@ -122,6 +124,8 @@ export const enableUseMemoCacheHook = __EXPERIMENTAL__;
|
|||
|
||||
export const enableUseEventHook = __EXPERIMENTAL__;
|
||||
|
||||
export const enableUnifiedSyncLane = __EXPERIMENTAL__;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Chopping Block
|
||||
//
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export const enableProfilerNestedUpdateScheduledHook = false;
|
|||
export const enableUpdaterTracking = __PROFILE__;
|
||||
export const enableCache = false;
|
||||
export const enableCacheElement = true;
|
||||
export const enableFrameEndScheduling = false;
|
||||
export const enableSchedulerDebugging = false;
|
||||
export const debugRenderPhaseSideEffectsForStrictMode = true;
|
||||
export const disableJavaScriptURLs = false;
|
||||
|
|
@ -87,5 +88,7 @@ export const enableFloat = false;
|
|||
|
||||
export const useModernStrictMode = false;
|
||||
|
||||
export const enableUnifiedSyncLane = false;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export const enableProfilerNestedUpdateScheduledHook = false;
|
|||
export const enableUpdaterTracking = __PROFILE__;
|
||||
export const enableCache = false;
|
||||
export const enableCacheElement = false;
|
||||
export const enableFrameEndScheduling = false;
|
||||
export const disableJavaScriptURLs = false;
|
||||
export const disableCommentsAsDOMContainers = true;
|
||||
export const disableInputAttributeSyncing = false;
|
||||
|
|
@ -76,5 +77,7 @@ export const enableFloat = false;
|
|||
|
||||
export const useModernStrictMode = false;
|
||||
|
||||
export const enableUnifiedSyncLane = false;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ export const enableProfilerNestedUpdateScheduledHook = false;
|
|||
export const enableUpdaterTracking = false;
|
||||
export const enableCache = __EXPERIMENTAL__;
|
||||
export const enableCacheElement = __EXPERIMENTAL__;
|
||||
export const enableFrameEndScheduling = __EXPERIMENTAL__;
|
||||
export const enableUnifiedSyncLane = __EXPERIMENTAL__;
|
||||
export const disableJavaScriptURLs = false;
|
||||
export const disableCommentsAsDOMContainers = true;
|
||||
export const disableInputAttributeSyncing = false;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export const enableProfilerNestedUpdateScheduledHook = false;
|
|||
export const enableUpdaterTracking = false;
|
||||
export const enableCache = true;
|
||||
export const enableCacheElement = true;
|
||||
export const enableFrameEndScheduling = false;
|
||||
export const disableJavaScriptURLs = false;
|
||||
export const disableCommentsAsDOMContainers = true;
|
||||
export const disableInputAttributeSyncing = false;
|
||||
|
|
@ -57,6 +58,7 @@ export const enableClientRenderFallbackOnTextMismatch = true;
|
|||
export const enableStrictEffects = false;
|
||||
export const createRootStrictEffectsByDefault = false;
|
||||
export const enableUseRefAccessWarning = false;
|
||||
export const enableUnifiedSyncLane = false;
|
||||
|
||||
export const disableSchedulerTimeoutInWorkLoop = false;
|
||||
export const enableLazyContextPropagation = false;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export const enableProfilerNestedUpdateScheduledHook = false;
|
|||
export const enableUpdaterTracking = false;
|
||||
export const enableCache = true;
|
||||
export const enableCacheElement = true;
|
||||
export const enableFrameEndScheduling = false;
|
||||
export const enableSchedulerDebugging = false;
|
||||
export const disableJavaScriptURLs = false;
|
||||
export const disableCommentsAsDOMContainers = true;
|
||||
|
|
@ -78,5 +79,7 @@ export const enableFloat = false;
|
|||
|
||||
export const useModernStrictMode = false;
|
||||
|
||||
export const enableUnifiedSyncLane = false;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ export const enableProfilerNestedUpdateScheduledHook = false;
|
|||
export const enableUpdaterTracking = false;
|
||||
export const enableCache = __EXPERIMENTAL__;
|
||||
export const enableCacheElement = __EXPERIMENTAL__;
|
||||
export const enableFrameEndScheduling = __EXPERIMENTAL__;
|
||||
export const enableUnifiedSyncLane = __EXPERIMENTAL__;
|
||||
export const disableJavaScriptURLs = false;
|
||||
export const disableCommentsAsDOMContainers = true;
|
||||
export const disableInputAttributeSyncing = false;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export const enableProfilerNestedUpdateScheduledHook = false;
|
|||
export const enableUpdaterTracking = false;
|
||||
export const enableCache = true;
|
||||
export const enableCacheElement = true;
|
||||
export const enableFrameEndScheduling = false;
|
||||
export const disableJavaScriptURLs = true;
|
||||
export const disableCommentsAsDOMContainers = true;
|
||||
export const disableInputAttributeSyncing = false;
|
||||
|
|
@ -77,5 +78,7 @@ export const enableFloat = false;
|
|||
|
||||
export const useModernStrictMode = false;
|
||||
|
||||
export const enableUnifiedSyncLane = false;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ export const consoleManagedByDevToolsDuringStrictMode = __VARIANT__;
|
|||
export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = __VARIANT__;
|
||||
export const enableClientRenderFallbackOnTextMismatch = __VARIANT__;
|
||||
export const enableTransitionTracing = __VARIANT__;
|
||||
export const enableFrameEndScheduling = __VARIANT__;
|
||||
export const enableUnifiedSyncLane = __VARIANT__;
|
||||
// Enable this flag to help with concurrent mode debugging.
|
||||
// It logs information to the console about React scheduling, rendering, and commit phases.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -33,7 +33,9 @@ export const {
|
|||
enableSyncDefaultUpdates,
|
||||
enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
|
||||
enableClientRenderFallbackOnTextMismatch,
|
||||
enableFrameEndScheduling,
|
||||
enableTransitionTracing,
|
||||
enableUnifiedSyncLane,
|
||||
} = dynamicFeatureFlags;
|
||||
|
||||
// On WWW, __EXPERIMENTAL__ is used for a new modern build.
|
||||
|
|
|
|||
|
|
@ -454,7 +454,13 @@ describe('useSubscription', () => {
|
|||
observableA.next('a-2');
|
||||
|
||||
// Update again
|
||||
renderer.update(<Parent observed={observableA} />);
|
||||
if (gate(flags => flags.enableUnifiedSyncLane)) {
|
||||
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([
|
||||
|
|
|
|||
|
|
@ -35,4 +35,28 @@ if (typeof window !== 'undefined') {
|
|||
global.cancelIdleCallback = function(callbackID) {
|
||||
clearTimeout(callbackID);
|
||||
};
|
||||
|
||||
// We need to mock rAF because Jest 26 does not flush rAF.
|
||||
// Once we upgrade to Jest 27+, rAF is flushed every 16ms.
|
||||
global.requestAnimationFrameQueue = null;
|
||||
global.requestAnimationFrame = function(callback) {
|
||||
if (global.requestAnimationFrameQueue == null) {
|
||||
global.requestAnimationFrameQueue = [];
|
||||
}
|
||||
global.requestAnimationFrameQueue.push(callback);
|
||||
return global.requestAnimationFrameQueue.length - 1;
|
||||
};
|
||||
|
||||
global.cancelAnimationFrame = function(id) {
|
||||
if (global.requestAnimationFrameQueue != null) {
|
||||
global.requestAnimationFrameQueue.splice(id, 1);
|
||||
}
|
||||
};
|
||||
|
||||
global.flushRequestAnimationFrameQueue = function() {
|
||||
if (global.requestAnimationFrameQueue != null) {
|
||||
global.requestAnimationFrameQueue.forEach(callback => callback());
|
||||
global.requestAnimationFrameQueue = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
|
|||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
if (global.requestAnimationFrameQueue != null) {
|
||||
console.warn('requestAnimationFrameQueue has not been flushed.');
|
||||
}
|
||||
});
|
||||
env.beforeEach(() => {
|
||||
global.requestAnimationFrameQueue = null;
|
||||
});
|
||||
|
||||
// TODO: Consider consolidating this with `yieldValue`. In both cases, tests
|
||||
|
|
|
|||
Loading…
Reference in New Issue