diff --git a/fixtures/scheduler/index.html b/fixtures/scheduler/index.html
index 5da9d66163010..4bc57fbd9a7cf 100644
--- a/fixtures/scheduler/index.html
+++ b/fixtures/scheduler/index.html
@@ -91,19 +91,8 @@
Tests:
If the counter advanced while you were away from this tab, it's correct.
- Can pause execution, dump scheduled callbacks, and continue where it left off
-
- Click the button above, press "continue" to finish the test after it pauses:
-
- Expected:
-
-
- -------------------------------------------------
- If the test didn't progress until you hit "continue" and
- you see the same above and below afterwards it's correct.
-
-------------------------------------------------
-
Actual:
-
+
Test Eight Removed
+
Test 8 was removed because it was testing a feature that was removed from the scheduler.
Can force a specific framerate
@@ -156,9 +145,6 @@ Tests:
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,
@@ -587,50 +573,6 @@ Tests:
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
diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js
index 1c673bf2543a0..5cef2d28437b0 100644
--- a/packages/react-dom/src/client/ReactDOMRoot.js
+++ b/packages/react-dom/src/client/ReactDOMRoot.js
@@ -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.',
@@ -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().',
diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js
index 12b110b16d3c9..99dbea044690f 100644
--- a/packages/react-reconciler/src/ReactFiberHooks.js
+++ b/packages/react-reconciler/src/ReactFiberHooks.js
@@ -3626,7 +3626,9 @@ function dispatchReducerAction(
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 ' +
@@ -3666,7 +3668,9 @@ function dispatchSetState(
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 ' +
diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js
index 42ffdf1ef0d2a..5a27f006179ba 100644
--- a/packages/react-server/src/ReactFlightServer.js
+++ b/packages/react-server/src/ReactFlightServer.js
@@ -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
diff --git a/packages/react/src/__tests__/React-hooks-arity.js b/packages/react/src/__tests__/React-hooks-arity.js
new file mode 100644
index 0000000000000..0ba63428d3989
--- /dev/null
+++ b/packages/react/src/__tests__/React-hooks-arity.js
@@ -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();
+ });
+
+ it("ensure useReducer setter's arity is correct", () => {
+ function Component() {
+ const [, dispatch] = React.useReducer(() => 'Halo!');
+
+ expect(dispatch.length).toBe(1);
+ return null;
+ }
+
+ ReactNoop.render();
+ });
+});
diff --git a/packages/react/src/__tests__/ReactStrictMode-test.internal.js b/packages/react/src/__tests__/ReactStrictMode-test.internal.js
index ed9ee70279648..2867e06483792 100644
--- a/packages/react/src/__tests__/ReactStrictMode-test.internal.js
+++ b/packages/react/src/__tests__/ReactStrictMode-test.internal.js
@@ -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(
+
+
+
+ ,
+
+ ,
+ ,
+ );
+ });
+
+ 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',
+ ]);
+ });
}
});
});
diff --git a/packages/scheduler/src/SchedulerFeatureFlags.js b/packages/scheduler/src/SchedulerFeatureFlags.js
index a5166f9813193..6828d0105ebb5 100644
--- a/packages/scheduler/src/SchedulerFeatureFlags.js
+++ b/packages/scheduler/src/SchedulerFeatureFlags.js
@@ -7,7 +7,6 @@
* @flow strict
*/
-export const enableSchedulerDebugging = false;
export const enableProfiling = false;
export const frameYieldMs = 5;
diff --git a/packages/scheduler/src/forks/Scheduler.js b/packages/scheduler/src/forks/Scheduler.js
index 3fe4d1720fc38..8b34f26bd0551 100644
--- a/packages/scheduler/src/forks/Scheduler.js
+++ b/packages/scheduler/src/forks/Scheduler.js
@@ -12,7 +12,6 @@
import type {PriorityLevel} from '../SchedulerPriorities';
import {
- enableSchedulerDebugging,
enableProfiling,
frameYieldMs,
userBlockingPriorityTimeout,
@@ -83,9 +82,6 @@ var timerQueue: Array = [];
// 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;
@@ -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.
@@ -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) {
@@ -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,
};
diff --git a/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js b/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js
index 1d6b955eb8888..2873dee099787 100644
--- a/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js
+++ b/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js
@@ -12,7 +12,6 @@ const dynamicFeatureFlags = require('SchedulerFeatureFlags');
export const {enableRequestPaint} = dynamicFeatureFlags;
-export const enableSchedulerDebugging = false;
export const enableProfiling = __DEV__;
export const frameYieldMs = 10;
diff --git a/packages/scheduler/src/forks/SchedulerMock.js b/packages/scheduler/src/forks/SchedulerMock.js
index 7f148c45be3e4..b638bf1eaf519 100644
--- a/packages/scheduler/src/forks/SchedulerMock.js
+++ b/packages/scheduler/src/forks/SchedulerMock.js
@@ -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?
@@ -72,9 +69,6 @@ var timerQueue: Array = [];
// 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;
@@ -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())
@@ -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) {
@@ -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,
diff --git a/packages/scheduler/src/forks/SchedulerNative.js b/packages/scheduler/src/forks/SchedulerNative.js
index 3832cbc69753e..33f9ae3313534 100644
--- a/packages/scheduler/src/forks/SchedulerNative.js
+++ b/packages/scheduler/src/forks/SchedulerNative.js
@@ -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;
diff --git a/packages/scheduler/src/forks/SchedulerPostTask.js b/packages/scheduler/src/forks/SchedulerPostTask.js
index a029fce0cbfcc..7465c38b92f8a 100644
--- a/packages/scheduler/src/forks/SchedulerPostTask.js
+++ b/packages/scheduler/src/forks/SchedulerPostTask.js
@@ -234,13 +234,5 @@ export function unstable_wrapCallback(callback: () => T): () => T {
export function unstable_forceFrameRate() {}
-export function unstable_pauseExecution() {}
-
-export function unstable_continueExecution() {}
-
-export function unstable_getFirstCallbackNode(): null {
- return null;
-}
-
// Currently no profiling build
export const unstable_Profiling = null;
diff --git a/scripts/jest/setupTests.www.js b/scripts/jest/setupTests.www.js
index efee213861ca6..b0b653bb78ff9 100644
--- a/scripts/jest/setupTests.www.js
+++ b/scripts/jest/setupTests.www.js
@@ -34,9 +34,9 @@ jest.mock('scheduler/src/SchedulerFeatureFlags', () => {
schedulerSrcPath + '/src/forks/SchedulerFeatureFlags.www'
);
- // These flags are not a dynamic on www, but we still want to run
- // tests in both versions.
- actual.enableSchedulerDebugging = __VARIANT__;
+ // Add flags here that are not a dynamic on www,
+ // but we still want to run tests in both versions.
+ //
return actual;
});
diff --git a/scripts/rollup/validate/eslintrc.cjs.js b/scripts/rollup/validate/eslintrc.cjs.js
index c91c298edf1be..b974ecee0d903 100644
--- a/scripts/rollup/validate/eslintrc.cjs.js
+++ b/scripts/rollup/validate/eslintrc.cjs.js
@@ -86,6 +86,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
+ 'no-restricted-syntax': [
+ 'error',
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
+ {
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
+ message:
+ 'Google Closure Compiler optimized `arguments` access. ' +
+ 'This affects function arity. ' +
+ 'Create a reference to `arguments` to avoid this optimization',
+ },
+ ],
},
// These plugins aren't used, but eslint complains if an eslint-ignore comment
diff --git a/scripts/rollup/validate/eslintrc.cjs2015.js b/scripts/rollup/validate/eslintrc.cjs2015.js
index 01f387e1d7188..6efc8838f0326 100644
--- a/scripts/rollup/validate/eslintrc.cjs2015.js
+++ b/scripts/rollup/validate/eslintrc.cjs2015.js
@@ -81,6 +81,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
+ 'no-restricted-syntax': [
+ 'error',
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
+ {
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
+ message:
+ 'Google Closure Compiler optimized `arguments` access. ' +
+ 'This affects function arity. ' +
+ 'Create a reference to `arguments` to avoid this optimization',
+ },
+ ],
},
// These plugins aren't used, but eslint complains if an eslint-ignore comment
diff --git a/scripts/rollup/validate/eslintrc.esm.js b/scripts/rollup/validate/eslintrc.esm.js
index 9f9938f204d21..20b5341a82ad8 100644
--- a/scripts/rollup/validate/eslintrc.esm.js
+++ b/scripts/rollup/validate/eslintrc.esm.js
@@ -83,6 +83,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
+ 'no-restricted-syntax': [
+ 'error',
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
+ {
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
+ message:
+ 'Google Closure Compiler optimized `arguments` access. ' +
+ 'This affects function arity. ' +
+ 'Create a reference to `arguments` to avoid this optimization',
+ },
+ ],
},
// These plugins aren't used, but eslint complains if an eslint-ignore comment
diff --git a/scripts/rollup/validate/eslintrc.fb.js b/scripts/rollup/validate/eslintrc.fb.js
index 9f344c5aac3aa..9606d00b353ac 100644
--- a/scripts/rollup/validate/eslintrc.fb.js
+++ b/scripts/rollup/validate/eslintrc.fb.js
@@ -71,6 +71,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
+ 'no-restricted-syntax': [
+ 'error',
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
+ {
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
+ message:
+ 'Google Closure Compiler optimized `arguments` access. ' +
+ 'This affects function arity. ' +
+ 'Create a reference to `arguments` to avoid this optimization',
+ },
+ ],
},
// These plugins aren't used, but eslint complains if an eslint-ignore comment
diff --git a/scripts/rollup/validate/eslintrc.rn.js b/scripts/rollup/validate/eslintrc.rn.js
index d18516f802304..941b1d1872efd 100644
--- a/scripts/rollup/validate/eslintrc.rn.js
+++ b/scripts/rollup/validate/eslintrc.rn.js
@@ -73,6 +73,17 @@ module.exports = {
rules: {
'no-undef': 'error',
'no-shadow-restricted-names': 'error',
+ 'no-restricted-syntax': [
+ 'error',
+ // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
+ {
+ selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
+ message:
+ 'Google Closure Compiler optimized `arguments` access. ' +
+ 'This affects function arity. ' +
+ 'Create a reference to `arguments` to avoid this optimization',
+ },
+ ],
},
// These plugins aren't used, but eslint complains if an eslint-ignore comment