Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure function arity is preserved after build #31808

Merged
merged 8 commits into from
Dec 18, 2024
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 @@ -3629,7 +3629,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 @@ -3669,7 +3671,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
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 />);
});
});
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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. ' +
'Access `arguments.length` to avoid this optimization',
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.cjs2015.js
Original file line number Diff line number Diff line change
Expand Up @@ -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. ' +
'Access `arguments.length` to avoid this optimization',
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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. ' +
'Access `arguments.length` to avoid this optimization',
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.fb.js
Original file line number Diff line number Diff line change
Expand Up @@ -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. ' +
'Access `arguments.length` to avoid this optimization',
eps1lon marked this conversation as resolved.
Show resolved Hide resolved
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down
11 changes: 11 additions & 0 deletions scripts/rollup/validate/eslintrc.rn.js
Original file line number Diff line number Diff line change
Expand Up @@ -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. ' +
'Access `arguments.length` to avoid this optimization',
},
],
},

// These plugins aren't used, but eslint complains if an eslint-ignore comment
Expand Down
Loading