@samitouri / QOS-React / commits / 2bd1c756c6

Ensure function arity is preserved after build (#31808)

Co-authored-by: eps1lon <sebastian.silbermann@vercel.com>

David Sancho committed Dec 18, 2024 at 14:08 UTC 2bd1c756c6fffefb00cdb2986218fa2701ece82e
8 files changed +113 -6
packages/react-dom/src/client/ReactDOMRoot.js
+8 -4
@@ -106,17 +106,19 @@ ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =
106 }
107
108 if (__DEV__) {
109 - if (typeof arguments[1] === 'function') {
109 + // using a reference to `arguments` bails out of GCC optimizations which affect function arity
110 + const args = arguments;
111 + if (typeof args[1] === 'function') {
112 console.error(
113 'does not support the second callback argument. ' +
114 'To execute a side effect after rendering, declare it in a component body with useEffect().',
115 );
114 - } else if (isValidContainer(arguments[1])) {
116 + } else if (isValidContainer(args[1])) {
117 console.error(
118 'You passed a container to the second argument of root.render(...). ' +
119 "You don't need to pass it again since you already passed it to create the root.",
120 );
119 - } else if (typeof arguments[1] !== 'undefined') {
121 + } else if (typeof args[1] !== 'undefined') {
122 console.error(
123 'You passed a second argument to root.render(...) but it only accepts ' +
124 'one argument.',
@@ -131,7 +133,9 @@ ReactDOMHydrationRoot.prototype.unmount = ReactDOMRoot.prototype.unmount =
133 // $FlowFixMe[missing-this-annot]
134 function (): void {
135 if (__DEV__) {
134 - if (typeof arguments[0] === 'function') {
136 + // using a reference to `arguments` bails out of GCC optimizations which affect function arity
137 + const args = arguments;
138 + if (typeof args[0] === 'function') {
139 console.error(
140 'does not support a callback argument. ' +
141 'To execute a side effect after rendering, declare it in a component body with useEffect().',
packages/react-reconciler/src/ReactFiberHooks.js
+6 -2
@@ -3626,7 +3626,9 @@ function dispatchReducerAction<S, A>(
3626 action: A,
3627 ): void {
3628 if (__DEV__) {
3629 - if (typeof arguments[3] === 'function') {
3629 + // using a reference to `arguments` bails out of GCC optimizations which affect function arity
3630 + const args = arguments;
3631 + if (typeof args[3] === 'function') {
3632 console.error(
3633 "State updates from the useState() and useReducer() Hooks don't support the " +
3634 'second callback argument. To execute a side effect after ' +
@@ -3666,7 +3668,9 @@ function dispatchSetState<S, A>(
3668 action: A,
3669 ): void {
3670 if (__DEV__) {
3669 - if (typeof arguments[3] === 'function') {
3671 + // using a reference to `arguments` bails out of GCC optimizations which affect function arity
3672 + const args = arguments;
3673 + if (typeof args[3] === 'function') {
3674 console.error(
3675 "State updates from the useState() and useReducer() Hooks don't support the " +
3676 'second callback argument. To execute a side effect after ' +
packages/react/src/__tests__/React-hooks-arity.js new
+44
@@ -0,0 +1,44 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +let React;
13 +let ReactNoop;
14 +
15 +describe('arity', () => {
16 + beforeEach(() => {
17 + jest.resetModules();
18 +
19 + React = require('react');
20 + ReactNoop = require('react-noop-renderer');
21 + });
22 +
23 + it("ensure useState setter's arity is correct", () => {
24 + function Component() {
25 + const [, setState] = React.useState(() => 'Halo!');
26 +
27 + expect(setState.length).toBe(1);
28 + return null;
29 + }
30 +
31 + ReactNoop.render(<Component />);
32 + });
33 +
34 + it("ensure useReducer setter's arity is correct", () => {
35 + function Component() {
36 + const [, dispatch] = React.useReducer(() => 'Halo!');
37 +
38 + expect(dispatch.length).toBe(1);
39 + return null;
40 + }
41 +
42 + ReactNoop.render(<Component />);
43 + });
44 +});
scripts/rollup/validate/eslintrc.cjs.js
+11
@@ -86,6 +86,17 @@ module.exports = {
86 rules: {
87 'no-undef': 'error',
88 'no-shadow-restricted-names': 'error',
89 + 'no-restricted-syntax': [
90 + 'error',
91 + // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
92 + {
93 + selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
94 + message:
95 + 'Google Closure Compiler optimized `arguments` access. ' +
96 + 'This affects function arity. ' +
97 + 'Create a reference to `arguments` to avoid this optimization',
98 + },
99 + ],
100 },
101
102 // These plugins aren't used, but eslint complains if an eslint-ignore comment
scripts/rollup/validate/eslintrc.cjs2015.js
+11
@@ -81,6 +81,17 @@ module.exports = {
81 rules: {
82 'no-undef': 'error',
83 'no-shadow-restricted-names': 'error',
84 + 'no-restricted-syntax': [
85 + 'error',
86 + // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
87 + {
88 + selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
89 + message:
90 + 'Google Closure Compiler optimized `arguments` access. ' +
91 + 'This affects function arity. ' +
92 + 'Create a reference to `arguments` to avoid this optimization',
93 + },
94 + ],
95 },
96
97 // These plugins aren't used, but eslint complains if an eslint-ignore comment
scripts/rollup/validate/eslintrc.esm.js
+11
@@ -83,6 +83,17 @@ module.exports = {
83 rules: {
84 'no-undef': 'error',
85 'no-shadow-restricted-names': 'error',
86 + 'no-restricted-syntax': [
87 + 'error',
88 + // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
89 + {
90 + selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
91 + message:
92 + 'Google Closure Compiler optimized `arguments` access. ' +
93 + 'This affects function arity. ' +
94 + 'Create a reference to `arguments` to avoid this optimization',
95 + },
96 + ],
97 },
98
99 // These plugins aren't used, but eslint complains if an eslint-ignore comment
scripts/rollup/validate/eslintrc.fb.js
+11
@@ -71,6 +71,17 @@ module.exports = {
71 rules: {
72 'no-undef': 'error',
73 'no-shadow-restricted-names': 'error',
74 + 'no-restricted-syntax': [
75 + 'error',
76 + // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
77 + {
78 + selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
79 + message:
80 + 'Google Closure Compiler optimized `arguments` access. ' +
81 + 'This affects function arity. ' +
82 + 'Create a reference to `arguments` to avoid this optimization',
83 + },
84 + ],
85 },
86
87 // These plugins aren't used, but eslint complains if an eslint-ignore comment
scripts/rollup/validate/eslintrc.rn.js
+11
@@ -73,6 +73,17 @@ module.exports = {
73 rules: {
74 'no-undef': 'error',
75 'no-shadow-restricted-names': 'error',
76 + 'no-restricted-syntax': [
77 + 'error',
78 + // TODO: Can be removed once we upgrade GCC to a version without `optimizeArgumentsArray` optimization.
79 + {
80 + selector: 'Identifier[name=/^JSCompiler_OptimizeArgumentsArray_/]',
81 + message:
82 + 'Google Closure Compiler optimized `arguments` access. ' +
83 + 'This affects function arity. ' +
84 + 'Create a reference to `arguments` to avoid this optimization',
85 + },
86 + ],
87 },
88
89 // These plugins aren't used, but eslint complains if an eslint-ignore comment