Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 2 additions & 60 deletions fixtures/scheduler/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,8 @@ <h2>Tests:</h2>
<div> If the counter advanced while you were away from this tab, it's correct.</div>
</li>
<li>
<p>Can pause execution, dump scheduled callbacks, and continue where it left off</p>
<button onClick="runTestEight()">Run Test 8</button>
<div><b>Click the button above, press "continue" to finish the test after it pauses:</b></div>
<button onClick="continueTestEight()">continue</button>
<div><b>Expected:</b></div>
<div id="test-8-expected">
</div>
<div> -------------------------------------------------</div>
<div> If the test didn't progress until you hit "continue" and </div>
<div> you see the same above and below afterwards it's correct.
<div> -------------------------------------------------</div>
<div><b>Actual:</b></div>
<div id="test-8"></div>
<p>Test Eight Removed</p>
<p>Test 8 was removed because it was testing a feature that was removed from the scheduler.</p>
</li>
<li>
<p>Can force a specific framerate</p>
Expand Down Expand Up @@ -156,9 +145,6 @@ <h2>Tests:</h2>
unstable_scheduleCallback: scheduleCallback,
unstable_cancelCallback: cancelCallback,
unstable_now: now,
unstable_getFirstCallbackNode: getFirstCallbackNode,
unstable_pauseExecution: pauseExecution,
unstable_continueExecution: continueExecution,
unstable_forceFrameRate: forceFrameRate,
unstable_shouldYield: shouldYield,
unstable_NormalPriority: NormalPriority,
Expand Down Expand Up @@ -587,50 +573,6 @@ <h2>Tests:</h2>
scheduleCallback(NormalPriority, incrementCounterAndScheduleNextCallback);
}

function runTestEight() {
// Test 8
// Pauses execution, dumps the queue, and continues execution
clearTestResult(8);

function countNodesInStack(firstCallbackNode) {
var node = firstCallbackNode;
var count = 0;
if (node !== null) {
do {
count = count + 1;
node = node.next;
} while (node !== firstCallbackNode);
}
return count;
}

scheduleCallback(NormalPriority, () => {

// size should be 0
updateTestResult(8, `Queue size: ${countNodesInStack(getFirstCallbackNode())}.`);
updateTestResult(8, 'Pausing... press continue to resume.');
pauseExecution();

scheduleCallback(NormalPriority, function () {
updateTestResult(8, 'Finishing...');
displayTestResult(8);
})
scheduleCallback(NormalPriority, function () {
updateTestResult(8, 'Done!');
displayTestResult(8);
checkTestResult(8);
})

// new size should be 2 now
updateTestResult(8, `Queue size: ${countNodesInStack(getFirstCallbackNode())}.`);
displayTestResult(8);
});
}

function continueTestEight() {
continueExecution();
}

function runTestNine() {
clearTestResult(9);
// We have this to make sure that the thing that goes right after it can get a full frame
Expand Down
12 changes: 8 additions & 4 deletions packages/react-dom/src/client/ReactDOMRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,19 @@ ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =
}

if (__DEV__) {
if (typeof arguments[1] === 'function') {
// using a reference to `arguments` bails out of GCC optimizations which affect function arity
const args = arguments;
if (typeof args[1] === 'function') {
console.error(
'does not support the second callback argument. ' +
'To execute a side effect after rendering, declare it in a component body with useEffect().',
);
} else if (isValidContainer(arguments[1])) {
} else if (isValidContainer(args[1])) {
console.error(
'You passed a container to the second argument of root.render(...). ' +
"You don't need to pass it again since you already passed it to create the root.",
);
} else if (typeof arguments[1] !== 'undefined') {
} else if (typeof args[1] !== 'undefined') {
console.error(
'You passed a second argument to root.render(...) but it only accepts ' +
'one argument.',
Expand All @@ -131,7 +133,9 @@ ReactDOMHydrationRoot.prototype.unmount = ReactDOMRoot.prototype.unmount =
// $FlowFixMe[missing-this-annot]
function (): void {
if (__DEV__) {
if (typeof arguments[0] === 'function') {
// using a reference to `arguments` bails out of GCC optimizations which affect function arity
const args = arguments;
if (typeof args[0] === 'function') {
console.error(
'does not support a callback argument. ' +
'To execute a side effect after rendering, declare it in a component body with useEffect().',
Expand Down
8 changes: 6 additions & 2 deletions packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3626,7 +3626,9 @@ function dispatchReducerAction<S, A>(
action: A,
): void {
if (__DEV__) {
if (typeof arguments[3] === 'function') {
// using a reference to `arguments` bails out of GCC optimizations which affect function arity
const args = arguments;
if (typeof args[3] === 'function') {
console.error(
"State updates from the useState() and useReducer() Hooks don't support the " +
'second callback argument. To execute a side effect after ' +
Expand Down Expand Up @@ -3666,7 +3668,9 @@ function dispatchSetState<S, A>(
action: A,
): void {
if (__DEV__) {
if (typeof arguments[3] === 'function') {
// using a reference to `arguments` bails out of GCC optimizations which affect function arity
const args = arguments;
if (typeof args[3] === 'function') {
console.error(
"State updates from the useState() and useReducer() Hooks don't support the " +
'second callback argument. To execute a side effect after ' +
Expand Down
2 changes: 1 addition & 1 deletion packages/react-server/src/ReactFlightServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3810,11 +3810,11 @@ function forwardDebugInfo(
debugInfo: ReactDebugInfo,
) {
for (let i = 0; i < debugInfo.length; i++) {
request.pendingChunks++;
if (typeof debugInfo[i].time === 'number') {
// When forwarding time we need to ensure to convert it to the time space of the payload.
emitTimingChunk(request, id, debugInfo[i].time);
} else {
request.pendingChunks++;
if (typeof debugInfo[i].name === 'string') {
// We outline this model eagerly so that we can refer to by reference as an owner.
// If we had a smarter way to dedupe we might not have to do this if there ends up
Expand Down
44 changes: 44 additions & 0 deletions packages/react/src/__tests__/React-hooks-arity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/

'use strict';

let React;
let ReactNoop;

describe('arity', () => {
beforeEach(() => {
jest.resetModules();

React = require('react');
ReactNoop = require('react-noop-renderer');
});

it("ensure useState setter's arity is correct", () => {
function Component() {
const [, setState] = React.useState(() => 'Halo!');

expect(setState.length).toBe(1);
return null;
}

ReactNoop.render(<Component />);
});

it("ensure useReducer setter's arity is correct", () => {
function Component() {
const [, dispatch] = React.useReducer(() => 'Halo!');

expect(dispatch.length).toBe(1);
return null;
}

ReactNoop.render(<Component />);
});
});
34 changes: 34 additions & 0 deletions packages/react/src/__tests__/ReactStrictMode-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,40 @@ describe('ReactStrictMode', () => {
'B: useEffect mount',
]);
});

it('should support nested strict mode on initial mount', async () => {
function Wrapper({children}) {
return children;
}
await act(() => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
root.render(
<Wrapper>
<Component label="A" />
<React.StrictMode>
<Component label="B" />,
</React.StrictMode>
,
</Wrapper>,
);
});

expect(log).toEqual([
'A: render',
'B: render',
'B: render',
'A: useLayoutEffect mount',
'B: useLayoutEffect mount',
'A: useEffect mount',
'B: useEffect mount',
// TODO: this is currently broken
// 'B: useLayoutEffect unmount',
// 'B: useEffect unmount',
// 'B: useLayoutEffect mount',
// 'B: useEffect mount',
]);
});
}
});
});
1 change: 0 additions & 1 deletion packages/scheduler/src/SchedulerFeatureFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
* @flow strict
*/

export const enableSchedulerDebugging = false;
export const enableProfiling = false;
export const frameYieldMs = 5;

Expand Down
28 changes: 1 addition & 27 deletions packages/scheduler/src/forks/Scheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import type {PriorityLevel} from '../SchedulerPriorities';

import {
enableSchedulerDebugging,
enableProfiling,
frameYieldMs,
userBlockingPriorityTimeout,
Expand Down Expand Up @@ -83,9 +82,6 @@ var timerQueue: Array<Task> = [];
// Incrementing id counter. Used to maintain insertion order.
var taskIdCounter = 1;

// Pausing the scheduler is useful for debugging.
var isSchedulerPaused = false;

var currentTask = null;
var currentPriorityLevel = NormalPriority;

Expand Down Expand Up @@ -193,10 +189,7 @@ function workLoop(initialTime: number) {
let currentTime = initialTime;
advanceTimers(currentTime);
currentTask = peek(taskQueue);
while (
currentTask !== null &&
!(enableSchedulerDebugging && isSchedulerPaused)
) {
while (currentTask !== null) {
if (!enableAlwaysYieldScheduler) {
if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
// This currentTask hasn't expired, and we've reached the deadline.
Expand Down Expand Up @@ -422,22 +415,6 @@ function unstable_scheduleCallback(
return newTask;
}

function unstable_pauseExecution() {
isSchedulerPaused = true;
}

function unstable_continueExecution() {
isSchedulerPaused = false;
if (!isHostCallbackScheduled && !isPerformingWork) {
isHostCallbackScheduled = true;
requestHostCallback();
}
}

function unstable_getFirstCallbackNode(): Task | null {
return peek(taskQueue);
}

function unstable_cancelCallback(task: Task) {
if (enableProfiling) {
if (task.isQueued) {
Expand Down Expand Up @@ -606,9 +583,6 @@ export {
unstable_getCurrentPriorityLevel,
shouldYieldToHost as unstable_shouldYield,
requestPaint as unstable_requestPaint,
unstable_continueExecution,
unstable_pauseExecution,
unstable_getFirstCallbackNode,
getCurrentTime as unstable_now,
forceFrameRate as unstable_forceFrameRate,
};
Expand Down
1 change: 0 additions & 1 deletion packages/scheduler/src/forks/SchedulerFeatureFlags.www.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const dynamicFeatureFlags = require('SchedulerFeatureFlags');

export const {enableRequestPaint} = dynamicFeatureFlags;

export const enableSchedulerDebugging = false;
export const enableProfiling = __DEV__;
export const frameYieldMs = 10;

Expand Down
32 changes: 2 additions & 30 deletions packages/scheduler/src/forks/SchedulerMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@

import type {PriorityLevel} from '../SchedulerPriorities';

import {
enableSchedulerDebugging,
enableProfiling,
} from '../SchedulerFeatureFlags';
import {enableProfiling} from '../SchedulerFeatureFlags';
import {push, pop, peek} from '../SchedulerMinHeap';

// TODO: Use symbols?
Expand Down Expand Up @@ -72,9 +69,6 @@ var timerQueue: Array<Task> = [];
// Incrementing id counter. Used to maintain insertion order.
var taskIdCounter = 1;

// Pausing the scheduler is useful for debugging.
var isSchedulerPaused = false;

var currentTask = null;
var currentPriorityLevel = NormalPriority;

Expand Down Expand Up @@ -195,10 +189,7 @@ function workLoop(hasTimeRemaining: boolean, initialTime: number): boolean {
let currentTime = initialTime;
advanceTimers(currentTime);
currentTask = peek(taskQueue);
while (
currentTask !== null &&
!(enableSchedulerDebugging && isSchedulerPaused)
) {
while (currentTask !== null) {
if (
currentTask.expirationTime > currentTime &&
(!hasTimeRemaining || shouldYieldToHost())
Expand Down Expand Up @@ -422,22 +413,6 @@ function unstable_scheduleCallback(
return newTask;
}

function unstable_pauseExecution() {
isSchedulerPaused = true;
}

function unstable_continueExecution() {
isSchedulerPaused = false;
if (!isHostCallbackScheduled && !isPerformingWork) {
isHostCallbackScheduled = true;
requestHostCallback(flushWork);
}
}

function unstable_getFirstCallbackNode(): Task | null {
return peek(taskQueue);
}

function unstable_cancelCallback(task: Task) {
if (enableProfiling) {
if (task.isQueued) {
Expand Down Expand Up @@ -679,9 +654,6 @@ export {
unstable_getCurrentPriorityLevel,
shouldYieldToHost as unstable_shouldYield,
requestPaint as unstable_requestPaint,
unstable_continueExecution,
unstable_pauseExecution,
unstable_getFirstCallbackNode,
getCurrentTime as unstable_now,
forceFrameRate as unstable_forceFrameRate,
unstable_flushAllWithoutAsserting,
Expand Down
3 changes: 0 additions & 3 deletions packages/scheduler/src/forks/SchedulerNative.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,6 @@ export const unstable_now: () => number | DOMHighResTimeStamp =
export const unstable_next: any = throwNotImplemented;
export const unstable_runWithPriority: any = throwNotImplemented;
export const unstable_wrapCallback: any = throwNotImplemented;
export const unstable_continueExecution: any = throwNotImplemented;
export const unstable_pauseExecution: any = throwNotImplemented;
export const unstable_getFirstCallbackNode: any = throwNotImplemented;
export const unstable_forceFrameRate: any = throwNotImplemented;
export const unstable_Profiling: any = null;

Expand Down
Loading
Loading