Remove enableRefAsProp feature flag (#30346)
The flag is fully rolled out.
Jan Kassens committed
Nov 4, 2024 at 14:30 UTC
07aa494432e97f63fca9faf2fad6f76fead31063
29 files changed
+113
-859
packages/jest-react/src/JestReact.js
+2
-2
@@ -6,7 +6,7 @@
6
*/
7
8
import {REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
9
-import {disableStringRefs, enableRefAsProp} from 'shared/ReactFeatureFlags';
9
+import {disableStringRefs} from 'shared/ReactFeatureFlags';
10
const {assertConsoleLogsCleared} = require('internal-test-utils/consoleMock');
11
12
import isArray from 'shared/isArray';
@@ -42,7 +42,7 @@ function assertYieldsWereCleared(root) {
42
}
43
44
function createJSXElementForTestComparison(type, props) {
45
- if (__DEV__ && enableRefAsProp) {
45
+ if (__DEV__) {
46
const element = {
47
$$typeof: REACT_ELEMENT_TYPE,
48
type: type,
packages/react-client/src/ReactFlightClient.js
+1
-2
@@ -44,7 +44,6 @@ import {
44
disableStringRefs,
45
enableBinaryFlight,
46
enablePostpone,
47
- enableRefAsProp,
47
enableFlightReadableStream,
48
enableOwnerStacks,
49
enableServerComponentLogs,
@@ -676,7 +675,7 @@ function createElement(
675
| React$Element<any>
676
| LazyComponent<React$Element<any>, SomeChunk<React$Element<any>>> {
677
let element: any;
679
- if (__DEV__ && enableRefAsProp) {
678
+ if (__DEV__) {
679
// `ref` is non-enumerable in dev
680
element = ({
681
$$typeof: REACT_ELEMENT_TYPE,
packages/react-devtools-shared/src/__tests__/legacy/storeLegacy-v15-test.js
-83
@@ -868,89 +868,6 @@ describe('Store (legacy)', () => {
868
`);
869
});
870
871
- // TODO: These tests don't work when enableRefAsProp is on because the
872
- // JSX runtime that's injected into the test environment by the compiler
873
- // is not compatible with older versions of React. Need to configure the
874
- // the test environment in such a way that certain test modules like this
875
- // one can use an older transform.
876
- if (!require('shared/ReactFeatureFlags').enableRefAsProp) {
877
- it('should support expanding deep parts of the tree', () => {
878
- const Wrapper = ({forwardedRef}) =>
879
- React.createElement(Nested, {
880
- depth: 3,
881
- forwardedRef: forwardedRef,
882
- });
883
- const Nested = ({depth, forwardedRef}) =>
884
- depth > 0
885
- ? React.createElement(Nested, {
886
- depth: depth - 1,
887
- forwardedRef: forwardedRef,
888
- })
889
- : React.createElement('div', {
890
- ref: forwardedRef,
891
- });
892
- let ref = null;
893
- const refSetter = value => {
894
- ref = value;
895
- };
896
- act(() =>
897
- ReactDOM.render(
898
- React.createElement(Wrapper, {
899
- forwardedRef: refSetter,
900
- }),
901
- document.createElement('div'),
902
- ),
903
- );
904
- expect(store).toMatchInlineSnapshot(`
905
- [root]
906
- ▸ <Wrapper>
907
- `);
908
- const deepestedNodeID = global.agent.getIDForHostInstance(ref);
909
- act(() => store.toggleIsCollapsed(deepestedNodeID, false));
910
- expect(store).toMatchInlineSnapshot(`
911
- [root]
912
- ▾ <Wrapper>
913
- ▾ <Nested>
914
- ▾ <Nested>
915
- ▾ <Nested>
916
- ▾ <Nested>
917
- <div>
918
- `);
919
- const rootID = store.getElementIDAtIndex(0);
920
- act(() => store.toggleIsCollapsed(rootID, true));
921
- expect(store).toMatchInlineSnapshot(`
922
- [root]
923
- ▸ <Wrapper>
924
- `);
925
- act(() => store.toggleIsCollapsed(rootID, false));
926
- expect(store).toMatchInlineSnapshot(`
927
- [root]
928
- ▾ <Wrapper>
929
- ▾ <Nested>
930
- ▾ <Nested>
931
- ▾ <Nested>
932
- ▾ <Nested>
933
- <div>
934
- `);
935
- const id = store.getElementIDAtIndex(1);
936
- act(() => store.toggleIsCollapsed(id, true));
937
- expect(store).toMatchInlineSnapshot(`
938
- [root]
939
- ▾ <Wrapper>
940
- ▸ <Nested>
941
- `);
942
- act(() => store.toggleIsCollapsed(id, false));
943
- expect(store).toMatchInlineSnapshot(`
944
- [root]
945
- ▾ <Wrapper>
946
- ▾ <Nested>
947
- ▾ <Nested>
948
- ▾ <Nested>
949
- ▾ <Nested>
950
- <div>
951
- `);
952
- });
953
- }
871
it('should support reordering of children', () => {
872
const Root = ({children}) => React.createElement('div', null, children);
873
const Component = () => React.createElement('div', null);
packages/react-dom/src/__tests__/ReactFunctionComponent-test.js
-217
@@ -197,223 +197,6 @@ describe('ReactFunctionComponent', () => {
197
.rejects.toThrowError();
198
});
199
200
- // @gate !enableRefAsProp || !__DEV__
201
- it('should warn when given a string ref', async () => {
202
- function Indirection(props) {
203
- return <div>{props.children}</div>;
204
- }
205
-
206
- class ParentUsingStringRef extends React.Component {
207
- render() {
208
- return (
209
- <Indirection>
210
- <FunctionComponent name="A" ref="stateless" />
211
- </Indirection>
212
- );
213
- }
214
- }
215
-
216
- await expect(async () => {
217
- const container = document.createElement('div');
218
- const root = ReactDOMClient.createRoot(container);
219
- await act(() => {
220
- root.render(<ParentUsingStringRef />);
221
- });
222
- }).toErrorDev(
223
- 'Function components cannot be given refs. ' +
224
- 'Attempts to access this ref will fail. ' +
225
- 'Did you mean to use React.forwardRef()?\n\n' +
226
- 'Check the render method ' +
227
- 'of `ParentUsingStringRef`.\n' +
228
- ' in FunctionComponent (at **)\n' +
229
- ' in div (at **)\n' +
230
- ' in Indirection (at **)\n' +
231
- ' in ParentUsingStringRef (at **)',
232
- );
233
-
234
- // No additional warnings should be logged
235
- const container = document.createElement('div');
236
- const root = ReactDOMClient.createRoot(container);
237
- await act(() => {
238
- root.render(<ParentUsingStringRef />);
239
- });
240
- });
241
-
242
- // @gate !enableRefAsProp || !__DEV__
243
- it('should warn when given a function ref', async () => {
244
- function Indirection(props) {
245
- return <div>{props.children}</div>;
246
- }
247
-
248
- const ref = jest.fn();
249
- class ParentUsingFunctionRef extends React.Component {
250
- render() {
251
- return (
252
- <Indirection>
253
- <FunctionComponent name="A" ref={ref} />
254
- </Indirection>
255
- );
256
- }
257
- }
258
-
259
- await expect(async () => {
260
- const container = document.createElement('div');
261
- const root = ReactDOMClient.createRoot(container);
262
- await act(() => {
263
- root.render(<ParentUsingFunctionRef />);
264
- });
265
- }).toErrorDev(
266
- 'Function components cannot be given refs. ' +
267
- 'Attempts to access this ref will fail. ' +
268
- 'Did you mean to use React.forwardRef()?\n\n' +
269
- 'Check the render method ' +
270
- 'of `ParentUsingFunctionRef`.\n' +
271
- ' in FunctionComponent (at **)\n' +
272
- ' in div (at **)\n' +
273
- ' in Indirection (at **)\n' +
274
- ' in ParentUsingFunctionRef (at **)',
275
- );
276
- expect(ref).not.toHaveBeenCalled();
277
-
278
- // No additional warnings should be logged
279
- const container = document.createElement('div');
280
- const root = ReactDOMClient.createRoot(container);
281
- await act(() => {
282
- root.render(<ParentUsingFunctionRef />);
283
- });
284
- });
285
-
286
- // @gate !enableRefAsProp || !__DEV__
287
- it('deduplicates ref warnings based on element or owner', async () => {
288
- // When owner uses JSX, we can use exact line location to dedupe warnings
289
- class AnonymousParentUsingJSX extends React.Component {
290
- render() {
291
- return <FunctionComponent name="A" ref={() => {}} />;
292
- }
293
- }
294
-
295
- let instance1;
296
-
297
- await expect(async () => {
298
- const container = document.createElement('div');
299
- const root = ReactDOMClient.createRoot(container);
300
-
301
- await act(() => {
302
- root.render(
303
- <AnonymousParentUsingJSX ref={current => (instance1 = current)} />,
304
- );
305
- });
306
- }).toErrorDev('Function components cannot be given refs.');
307
- // Should be deduped (offending element is on the same line):
308
- instance1.forceUpdate();
309
- // Should also be deduped (offending element is on the same line):
310
- let container = document.createElement('div');
311
- let root = ReactDOMClient.createRoot(container);
312
- await act(() => {
313
- root.render(<AnonymousParentUsingJSX />);
314
- });
315
-
316
- // When owner doesn't use JSX, and is anonymous, we warn once per internal instance.
317
- class AnonymousParentNotUsingJSX extends React.Component {
318
- render() {
319
- return React.createElement(FunctionComponent, {
320
- name: 'A',
321
- ref: () => {},
322
- });
323
- }
324
- }
325
-
326
- let instance2;
327
- await expect(async () => {
328
- container = document.createElement('div');
329
- root = ReactDOMClient.createRoot(container);
330
- await act(() => {
331
- root.render(
332
- <AnonymousParentNotUsingJSX ref={current => (instance2 = current)} />,
333
- );
334
- });
335
- }).toErrorDev('Function components cannot be given refs.');
336
- // Should be deduped (same internal instance, no additional warnings)
337
- instance2.forceUpdate();
338
- // Could not be differentiated (since owner is anonymous and no source location)
339
- container = document.createElement('div');
340
- root = ReactDOMClient.createRoot(container);
341
- await act(() => {
342
- root.render(<AnonymousParentNotUsingJSX />);
343
- });
344
-
345
- // When owner doesn't use JSX, but is named, we warn once per owner name
346
- class NamedParentNotUsingJSX extends React.Component {
347
- render() {
348
- return React.createElement(FunctionComponent, {
349
- name: 'A',
350
- ref: () => {},
351
- });
352
- }
353
- }
354
- let instance3;
355
- await expect(async () => {
356
- container = document.createElement('div');
357
- root = ReactDOMClient.createRoot(container);
358
- await act(() => {
359
- root.render(
360
- <NamedParentNotUsingJSX ref={current => (instance3 = current)} />,
361
- );
362
- });
363
- }).toErrorDev('Function components cannot be given refs.');
364
- // Should be deduped (same owner name, no additional warnings):
365
- instance3.forceUpdate();
366
- // Should also be deduped (same owner name, no additional warnings):
367
- container = document.createElement('div');
368
- root = ReactDOMClient.createRoot(container);
369
- await act(() => {
370
- root.render(<NamedParentNotUsingJSX />);
371
- });
372
- });
373
-
374
- // This guards against a regression caused by clearing the current debug fiber.
375
- // https://github.com/facebook/react/issues/10831
376
- // @gate !disableLegacyContext || !__DEV__
377
- // @gate !enableRefAsProp || !__DEV__
378
- it('should warn when giving a function ref with context', async () => {
379
- function Child() {
380
- return null;
381
- }
382
- Child.contextTypes = {
383
- foo: PropTypes.string,
384
- };
385
-
386
- class Parent extends React.Component {
387
- static childContextTypes = {
388
- foo: PropTypes.string,
389
- };
390
- getChildContext() {
391
- return {
392
- foo: 'bar',
393
- };
394
- }
395
- render() {
396
- return <Child ref={function () {}} />;
397
- }
398
- }
399
-
400
- await expect(async () => {
401
- const container = document.createElement('div');
402
- const root = ReactDOMClient.createRoot(container);
403
- await act(() => {
404
- root.render(<Parent />);
405
- });
406
- }).toErrorDev(
407
- 'Function components cannot be given refs. ' +
408
- 'Attempts to access this ref will fail. ' +
409
- 'Did you mean to use React.forwardRef()?\n\n' +
410
- 'Check the render method ' +
411
- 'of `Parent`.\n' +
412
- ' in Child (at **)\n' +
413
- ' in Parent (at **)',
414
- );
415
- });
416
-
200
it('should use correct name in key warning', async () => {
201
function Child() {
202
return <div>{[<span />]}</div>;
packages/react-dom/src/__tests__/refs-test.js
-18
@@ -369,24 +369,6 @@ describe('ref swapping', () => {
369
});
370
}).rejects.toThrow('Expected ref to be a function');
371
});
372
-
373
- // @gate !enableRefAsProp && www
374
- it('undefined ref on manually inlined React element triggers error', async () => {
375
- const container = document.createElement('div');
376
- const root = ReactDOMClient.createRoot(container);
377
- await expect(async () => {
378
- await act(() => {
379
- root.render({
380
- $$typeof: Symbol.for('react.element'),
381
- type: 'div',
382
- props: {
383
- ref: undefined,
384
- },
385
- key: null,
386
- });
387
- });
388
- }).rejects.toThrow('Expected ref to be a function');
389
- });
372
});
373
374
describe('root level refs', () => {
packages/react-noop-renderer/src/createReactNoop.js
+2
-6
@@ -35,11 +35,7 @@ import {
35
ConcurrentRoot,
36
LegacyRoot,
37
} from 'react-reconciler/constants';
38
-import {
39
- enableRefAsProp,
40
- disableLegacyMode,
41
- disableStringRefs,
42
-} from 'shared/ReactFeatureFlags';
38
+import {disableLegacyMode, disableStringRefs} from 'shared/ReactFeatureFlags';
39
40
import ReactSharedInternals from 'shared/ReactSharedInternals';
41
import ReactVersion from 'shared/ReactVersion';
@@ -833,7 +829,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
829
let currentEventPriority = DefaultEventPriority;
830
831
function createJSXElementForTestComparison(type, props) {
836
- if (__DEV__ && enableRefAsProp) {
832
+ if (__DEV__) {
833
const element = {
834
type: type,
835
$$typeof: REACT_ELEMENT_TYPE,
packages/react-reconciler/src/ReactChildFiber.js
+12
-41
@@ -45,7 +45,6 @@ import {
45
} from './ReactWorkTags';
46
import isArray from 'shared/isArray';
47
import {
48
- enableRefAsProp,
48
enableAsyncIterableChildren,
49
disableLegacyMode,
50
enableOwnerStacks,
@@ -239,21 +238,6 @@ function validateFragmentProps(
238
break;
239
}
240
}
242
-
243
- if (!enableRefAsProp && element.ref !== null) {
244
- if (fiber === null) {
245
- // For unkeyed root fragments there's no Fiber. We create a fake one just for
246
- // error stack handling.
247
- fiber = createFiberFromElement(element, returnFiber.mode, 0);
248
- if (__DEV__) {
249
- fiber._debugInfo = currentDebugInfo;
250
- }
251
- fiber.return = returnFiber;
252
- }
253
- runWithFiberInDEV(fiber, () => {
254
- console.error('Invalid attribute `ref` supplied to `React.Fragment`.');
255
- });
256
- }
241
}
242
}
243
@@ -266,27 +250,14 @@ function unwrapThenable<T>(thenable: Thenable<T>): T {
250
return trackUsedThenable(thenableState, thenable, index);
251
}
252
269
-function coerceRef(
270
- returnFiber: Fiber,
271
- current: Fiber | null,
272
- workInProgress: Fiber,
273
- element: ReactElement,
274
-): void {
275
- let ref;
276
- if (enableRefAsProp) {
277
- // TODO: This is a temporary, intermediate step. When enableRefAsProp is on,
278
- // we should resolve the `ref` prop during the begin phase of the component
279
- // it's attached to (HostComponent, ClassComponent, etc).
280
- const refProp = element.props.ref;
281
- ref = refProp !== undefined ? refProp : null;
282
- } else {
283
- // Old behavior.
284
- ref = element.ref;
285
- }
286
-
287
- // TODO: If enableRefAsProp is on, we shouldn't use the `ref` field. We
253
+function coerceRef(workInProgress: Fiber, element: ReactElement): void {
254
+ // TODO: This is a temporary, intermediate step. Now that enableRefAsProp is on,
255
+ // we should resolve the `ref` prop during the begin phase of the component
256
+ // it's attached to (HostComponent, ClassComponent, etc).
257
+ const refProp = element.props.ref;
258
+ // TODO: With enableRefAsProp now rolled out, we shouldn't use the `ref` field. We
259
// should always read the ref from the prop.
289
- workInProgress.ref = ref;
260
+ workInProgress.ref = refProp !== undefined ? refProp : null;
261
}
262
263
function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {
@@ -569,7 +540,7 @@ function createChildReconciler(
540
) {
541
// Move based on index
542
const existing = useFiber(current, element.props);
572
- coerceRef(returnFiber, current, existing, element);
543
+ coerceRef(existing, element);
544
existing.return = returnFiber;
545
if (__DEV__) {
546
existing._debugOwner = element._owner;
@@ -580,7 +551,7 @@ function createChildReconciler(
551
}
552
// Insert
553
const created = createFiberFromElement(element, returnFiber.mode, lanes);
583
- coerceRef(returnFiber, current, created, element);
554
+ coerceRef(created, element);
555
created.return = returnFiber;
556
if (__DEV__) {
557
created._debugInfo = currentDebugInfo;
@@ -693,7 +664,7 @@ function createChildReconciler(
664
returnFiber.mode,
665
lanes,
666
);
696
- coerceRef(returnFiber, null, created, newChild);
667
+ coerceRef(created, newChild);
668
created.return = returnFiber;
669
if (__DEV__) {
670
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
@@ -1684,7 +1655,7 @@ function createChildReconciler(
1655
) {
1656
deleteRemainingChildren(returnFiber, child.sibling);
1657
const existing = useFiber(child, element.props);
1687
- coerceRef(returnFiber, child, existing, element);
1658
+ coerceRef(existing, element);
1659
existing.return = returnFiber;
1660
if (__DEV__) {
1661
existing._debugOwner = element._owner;
@@ -1722,7 +1693,7 @@ function createChildReconciler(
1693
return created;
1694
} else {
1695
const created = createFiberFromElement(element, returnFiber.mode, lanes);
1725
- coerceRef(returnFiber, currentFirstChild, created, element);
1696
+ coerceRef(created, element);
1697
created.return = returnFiber;
1698
if (__DEV__) {
1699
created._debugInfo = currentDebugInfo;
packages/react-reconciler/src/ReactFiberBeginWork.js
+2
-27
@@ -108,7 +108,6 @@ import {
108
enableAsyncActions,
109
enablePostpone,
110
enableRenderableContext,
111
- enableRefAsProp,
111
disableLegacyMode,
112
disableDefaultPropsExceptForClasses,
113
disableStringRefs,
@@ -125,10 +124,7 @@ import {
124
REACT_MEMO_TYPE,
125
getIteratorFn,
126
} from 'shared/ReactSymbols';
128
-import {
129
- getCurrentFiberOwnerNameInDevOrNull,
130
- setCurrentFiber,
131
-} from './ReactCurrentFiber';
127
+import {setCurrentFiber} from './ReactCurrentFiber';
128
import {
129
resolveFunctionForHotReloading,
130
resolveForwardRefForHotReloading,
@@ -319,7 +315,6 @@ let didWarnAboutBadClass;
315
let didWarnAboutContextTypeOnFunctionComponent;
316
let didWarnAboutContextTypes;
317
let didWarnAboutGetDerivedStateOnFunctionComponent;
322
-let didWarnAboutFunctionRefs;
318
export let didWarnAboutReassigningProps: boolean;
319
let didWarnAboutRevealOrder;
320
let didWarnAboutTailOptions;
@@ -330,7 +325,6 @@ if (__DEV__) {
325
didWarnAboutContextTypeOnFunctionComponent = ({}: {[string]: boolean});
326
didWarnAboutContextTypes = ({}: {[string]: boolean});
327
didWarnAboutGetDerivedStateOnFunctionComponent = ({}: {[string]: boolean});
333
- didWarnAboutFunctionRefs = ({}: {[string]: boolean});
328
didWarnAboutReassigningProps = false;
329
didWarnAboutRevealOrder = ({}: {[empty]: boolean});
330
didWarnAboutTailOptions = ({}: {[string]: boolean});
@@ -416,7 +410,7 @@ function updateForwardRef(
410
const ref = workInProgress.ref;
411
412
let propsWithoutRef;
419
- if (enableRefAsProp && 'ref' in nextProps) {
413
+ if ('ref' in nextProps) {
414
// `ref` is just a prop now, but `forwardRef` expects it to not appear in
415
// the props object. This used to happen in the JSX runtime, but now we do
416
// it here.
@@ -1954,25 +1948,6 @@ function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) {
1948
Component.displayName || Component.name || 'Component',
1949
);
1950
}
1957
- if (!enableRefAsProp && workInProgress.ref !== null) {
1958
- let info = '';
1959
- const componentName = getComponentNameFromType(Component) || 'Unknown';
1960
- const ownerName = getCurrentFiberOwnerNameInDevOrNull();
1961
- if (ownerName) {
1962
- info += '\n\nCheck the render method of `' + ownerName + '`.';
1963
- }
1964
-
1965
- const warningKey = componentName + '|' + (ownerName || '');
1966
- if (!didWarnAboutFunctionRefs[warningKey]) {
1967
- didWarnAboutFunctionRefs[warningKey] = true;
1968
- console.error(
1969
- 'Function components cannot be given refs. ' +
1970
- 'Attempts to access this ref will fail. ' +
1971
- 'Did you mean to use React.forwardRef()?%s',
1972
- info,
1973
- );
1974
- }
1975
- }
1951
1952
if (
1953
!disableDefaultPropsExceptForClasses &&
packages/react-reconciler/src/ReactFiberClassComponent.js
+6
-9
@@ -23,7 +23,6 @@ import {
23
enableDebugTracing,
24
enableSchedulingProfiler,
25
enableLazyContextPropagation,
26
- enableRefAsProp,
26
disableDefaultPropsExceptForClasses,
27
} from 'shared/ReactFeatureFlags';
28
import ReactStrictModeWarnings from './ReactStrictModeWarnings';
@@ -1252,14 +1251,12 @@ export function resolveClassComponentProps(
1251
): Object {
1252
let newProps = baseProps;
1253
1255
- if (enableRefAsProp) {
1256
- // Remove ref from the props object, if it exists.
1257
- if ('ref' in baseProps) {
1258
- newProps = ({}: any);
1259
- for (const propName in baseProps) {
1260
- if (propName !== 'ref') {
1261
- newProps[propName] = baseProps[propName];
1262
- }
1254
+ // Remove ref from the props object, if it exists.
1255
+ if ('ref' in baseProps) {
1256
+ newProps = ({}: any);
1257
+ for (const propName in baseProps) {
1258
+ if (propName !== 'ref') {
1259
+ newProps[propName] = baseProps[propName];
1260
}
1261
}
1262
}
packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js
+1
-2
@@ -85,7 +85,6 @@ describe('ReactFiberRefs', () => {
85
expect(ref2.current).not.toBe(null);
86
});
87
88
- // @gate enableRefAsProp
88
// @gate !disableStringRefs
89
it('string ref props are converted to function refs', async () => {
90
let refProp;
@@ -105,7 +104,7 @@ describe('ReactFiberRefs', () => {
104
const root = ReactNoop.createRoot();
105
await act(() => root.render(<Owner />));
106
108
- // When string refs aren't disabled, and enableRefAsProp is on, string refs
107
+ // When string refs aren't disabled, string refs
108
// the receiving component receives a callback ref, not the original string.
109
// This behavior should never be shipped to open source; it's only here to
110
// allow Meta to keep using string refs temporarily while they finish
packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
+1
-14
@@ -1299,20 +1299,7 @@ describe('ReactIncrementalSideEffects', () => {
1299
1300
ReactNoop.render(<Foo show={true} />);
1301
1302
- if (gate(flags => flags.enableRefAsProp)) {
1303
- await waitForAll([]);
1304
- } else {
1305
- await expect(async () => await waitForAll([])).toErrorDev(
1306
- 'Function components cannot be given refs. ' +
1307
- 'Attempts to access this ref will fail. ' +
1308
- 'Did you mean to use React.forwardRef()?\n\n' +
1309
- 'Check the render method ' +
1310
- 'of `Foo`.\n' +
1311
- ' in FunctionComponent (at **)\n' +
1312
- ' in div (at **)\n' +
1313
- ' in Foo (at **)',
1314
- );
1315
- }
1302
+ await waitForAll([]);
1303
1304
expect(ops).toEqual([
1305
classInstance,
packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
-24
@@ -1236,30 +1236,6 @@ describe('ReactLazy', () => {
1236
expect(root).toMatchRenderedOutput('2');
1237
});
1238
1239
- // @gate !enableRefAsProp || !__DEV__
1240
- it('warns about ref on functions for lazy-loaded components', async () => {
1241
- const Foo = props => <div />;
1242
- const LazyFoo = lazy(() => {
1243
- return fakeImport(Foo);
1244
- });
1245
-
1246
- const ref = React.createRef();
1247
- ReactTestRenderer.create(
1248
- <Suspense fallback={<Text text="Loading..." />}>
1249
- <LazyFoo ref={ref} />
1250
- </Suspense>,
1251
- {
1252
- unstable_isConcurrent: true,
1253
- },
1254
- );
1255
-
1256
- await waitForAll(['Loading...']);
1257
- await resolveFakeImport(Foo);
1258
- await expect(async () => {
1259
- await waitForAll([]);
1260
- }).toErrorDev('Function components cannot be given refs');
1261
- });
1262
-
1239
it('should error with a component stack naming the resolved component', async () => {
1240
let componentStackMessage;
1241
packages/react-reconciler/src/__tests__/ReactMemo-test.js
-37
@@ -44,43 +44,6 @@ describe('memo', () => {
44
return {default: result};
45
}
46
47
- // @gate !enableRefAsProp || !__DEV__
48
- it('warns when giving a ref (simple)', async () => {
49
- // This test lives outside sharedTests because the wrappers don't forward
50
- // refs properly, and they end up affecting the current owner which is used
51
- // by the warning (making the messages not line up).
52
- function App() {
53
- return null;
54
- }
55
- App = React.memo(App);
56
- function Outer() {
57
- return <App ref={() => {}} />;
58
- }
59
- ReactNoop.render(<Outer />);
60
- await expect(async () => await waitForAll([])).toErrorDev([
61
- 'Function components cannot be given refs. Attempts to access ' +
62
- 'this ref will fail.',
63
- ]);
64
- });
65
-
66
- // @gate !enableRefAsProp || !__DEV__
67
- it('warns when giving a ref (complex)', async () => {
68
- function App() {
69
- return null;
70
- }
71
- // A custom compare function means this won't use SimpleMemoComponent (as of this writing)
72
- // SimpleMemoComponent is unobservable tho, so we can't check :)
73
- App = React.memo(App, () => false);
74
- function Outer() {
75
- return <App ref={() => {}} />;
76
- }
77
- ReactNoop.render(<Outer />);
78
- await expect(async () => await waitForAll([])).toErrorDev([
79
- 'Function components cannot be given refs. Attempts to access ' +
80
- 'this ref will fail.',
81
- ]);
82
- });
83
-
47
// Tests should run against both the lazy and non-lazy versions of `memo`.
48
// To make the tests work for both versions, we wrap the non-lazy version in
49
// a lazy function component.
packages/react-server/src/ReactFizzServer.js
+11
-20
@@ -160,7 +160,6 @@ import {
160
enablePostpone,
161
enableHalt,
162
enableRenderableContext,
163
- enableRefAsProp,
163
disableDefaultPropsExceptForClasses,
164
enableAsyncIterableChildren,
165
disableStringRefs,
@@ -1671,14 +1670,12 @@ export function resolveClassComponentProps(
1670
): Object {
1671
let newProps = baseProps;
1672
1674
- if (enableRefAsProp) {
1675
- // Remove ref from the props object, if it exists.
1676
- if ('ref' in baseProps) {
1677
- newProps = ({}: any);
1678
- for (const propName in baseProps) {
1679
- if (propName !== 'ref') {
1680
- newProps[propName] = baseProps[propName];
1681
- }
1673
+ // Remove ref from the props object, if it exists.
1674
+ if ('ref' in baseProps) {
1675
+ newProps = ({}: any);
1676
+ for (const propName in baseProps) {
1677
+ if (propName !== 'ref') {
1678
+ newProps[propName] = baseProps[propName];
1679
}
1680
}
1681
}
@@ -1973,7 +1970,7 @@ function renderForwardRef(
1970
ref: any,
1971
): void {
1972
let propsWithoutRef;
1976
- if (enableRefAsProp && 'ref' in props) {
1973
+ if ('ref' in props) {
1974
// `ref` is just a prop now, but `forwardRef` expects it to not appear in
1975
// the props object. This used to happen in the JSX runtime, but now we do
1976
// it here.
@@ -2595,16 +2592,10 @@ function retryNode(request: Request, task: Task): void {
2592
const key = element.key;
2593
const props = element.props;
2594
2598
- let ref;
2599
- if (enableRefAsProp) {
2600
- // TODO: This is a temporary, intermediate step. Once the feature
2601
- // flag is removed, we should get the ref off the props object right
2602
- // before using it.
2603
- const refProp = props.ref;
2604
- ref = refProp !== undefined ? refProp : null;
2605
- } else {
2606
- ref = element.ref;
2607
- }
2595
+ // TODO: We should get the ref off the props object right before using
2596
+ // it.
2597
+ const refProp = props.ref;
2598
+ const ref = refProp !== undefined ? refProp : null;
2599
2600
const debugTask: null | ConsoleTask =
2601
__DEV__ && enableOwnerStacks ? task.debugTask : null;
packages/react-server/src/ReactFlightServer.js
+4
-11
@@ -18,7 +18,6 @@ import {
18
enablePostpone,
19
enableHalt,
20
enableTaint,
21
- enableRefAsProp,
21
enableServerComponentLogs,
22
enableOwnerStacks,
23
} from 'shared/ReactFeatureFlags';
@@ -2512,16 +2511,10 @@ function renderModelDestructive(
2511
}
2512
2513
const props = element.props;
2515
- let ref;
2516
- if (enableRefAsProp) {
2517
- // TODO: This is a temporary, intermediate step. Once the feature
2518
- // flag is removed, we should get the ref off the props object right
2519
- // before using it.
2520
- const refProp = props.ref;
2521
- ref = refProp !== undefined ? refProp : null;
2522
- } else {
2523
- ref = element.ref;
2524
- }
2514
+ // TODO: We should get the ref off the props object right before using
2515
+ // it.
2516
+ const refProp = props.ref;
2517
+ const ref = refProp !== undefined ? refProp : null;
2518
2519
// Attempt to render the Server Component.
2520
packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js
+3
-38
@@ -386,39 +386,6 @@ describe('ReactTestRenderer', () => {
386
expect(log).toEqual([null]);
387
});
388
389
- // @gate !enableRefAsProp || !__DEV__
390
- it('warns correctly for refs on SFCs', async () => {
391
- function Bar() {
392
- return <div>Hello, world</div>;
393
- }
394
- class Foo extends React.Component {
395
- fooRef = React.createRef();
396
- render() {
397
- return <Bar ref={this.fooRef} />;
398
- }
399
- }
400
- class Baz extends React.Component {
401
- bazRef = React.createRef();
402
- render() {
403
- return <div ref={this.bazRef} />;
404
- }
405
- }
406
- await act(() => {
407
- ReactTestRenderer.create(<Baz />);
408
- });
409
- await expect(async () => {
410
- await act(() => {
411
- ReactTestRenderer.create(<Foo />);
412
- });
413
- }).toErrorDev(
414
- 'Function components cannot be given refs. Attempts ' +
415
- 'to access this ref will fail. ' +
416
- 'Did you mean to use React.forwardRef()?\n' +
417
- ' in Bar (at **)\n' +
418
- ' in Foo (at **)',
419
- );
420
- });
421
-
389
it('allows an optional createNodeMock function', async () => {
390
const mockDivInstance = {appendChild: () => {}};
391
const mockInputInstance = {focus: () => {}};
@@ -1226,11 +1193,9 @@ describe('ReactTestRenderer', () => {
1193
{
1194
instance: null,
1195
nodeType: 'host',
1229
- props: gate(flags => flags.enableRefAsProp)
1230
- ? {
1231
- ref: refFn,
1232
- }
1233
- : {},
1196
+ props: {
1197
+ ref: refFn,
1198
+ },
1199
rendered: [],
1200
type: 'span',
1201
},
packages/react/src/__tests__/ReactCreateElement-test.js
+12
-69
@@ -37,11 +37,7 @@ describe('ReactCreateElement', () => {
37
const element = React.createElement(ComponentClass);
38
expect(element.type).toBe(ComponentClass);
39
expect(element.key).toBe(null);
40
- if (gate(flags => flags.enableRefAsProp)) {
41
- expect(element.ref).toBe(null);
42
- } else {
43
- expect(element.ref).toBe(null);
44
- }
40
+ expect(element.ref).toBe(null);
41
if (__DEV__) {
42
expect(Object.isFrozen(element)).toBe(true);
43
expect(Object.isFrozen(element.props)).toBe(true);
@@ -90,45 +86,11 @@ describe('ReactCreateElement', () => {
86
);
87
});
88
93
- // @gate !enableRefAsProp || !__DEV__
94
- it('should warn when `ref` is being accessed', async () => {
95
- class Child extends React.Component {
96
- render() {
97
- return React.createElement('div', null, this.props.ref);
98
- }
99
- }
100
- class Parent extends React.Component {
101
- render() {
102
- return React.createElement(
103
- 'div',
104
- null,
105
- React.createElement(Child, {ref: React.createRef()}),
106
- );
107
- }
108
- }
109
- const root = ReactDOMClient.createRoot(document.createElement('div'));
110
-
111
- await expect(async () => {
112
- await act(() => {
113
- root.render(React.createElement(Parent));
114
- });
115
- }).toErrorDev(
116
- 'Child: `ref` is not a prop. Trying to access it will result ' +
117
- 'in `undefined` being returned. If you need to access the same ' +
118
- 'value within the child component, you should pass it as a different ' +
119
- 'prop. (https://react.dev/link/special-props)',
120
- );
121
- });
122
-
89
it('allows a string to be passed as the type', () => {
90
const element = React.createElement('div');
91
expect(element.type).toBe('div');
92
expect(element.key).toBe(null);
127
- if (gate(flags => flags.enableRefAsProp)) {
128
- expect(element.ref).toBe(null);
129
- } else {
130
- expect(element.ref).toBe(null);
131
- }
93
+ expect(element.ref).toBe(null);
94
if (__DEV__) {
95
expect(Object.isFrozen(element)).toBe(true);
96
expect(Object.isFrozen(element.props)).toBe(true);
@@ -179,20 +141,13 @@ describe('ReactCreateElement', () => {
141
foo: '56',
142
});
143
expect(element.type).toBe(ComponentClass);
182
- if (gate(flags => flags.enableRefAsProp)) {
183
- expect(() => expect(element.ref).toBe(ref)).toErrorDev(
184
- 'Accessing element.ref was removed in React 19',
185
- {withoutStack: true},
186
- );
187
- const expectation = {foo: '56', ref};
188
- Object.freeze(expectation);
189
- expect(element.props).toEqual(expectation);
190
- } else {
191
- const expectation = {foo: '56'};
192
- Object.freeze(expectation);
193
- expect(element.props).toEqual(expectation);
194
- expect(element.ref).toBe(ref);
195
- }
144
+ expect(() => expect(element.ref).toBe(ref)).toErrorDev(
145
+ 'Accessing element.ref was removed in React 19',
146
+ {withoutStack: true},
147
+ );
148
+ const expectation = {foo: '56', ref};
149
+ Object.freeze(expectation);
150
+ expect(element.props).toEqual(expectation);
151
});
152
153
it('extracts null key', () => {
@@ -218,11 +173,7 @@ describe('ReactCreateElement', () => {
173
const element = React.createElement(ComponentClass, props);
174
expect(element.type).toBe(ComponentClass);
175
expect(element.key).toBe(null);
221
- if (gate(flags => flags.enableRefAsProp)) {
222
- expect(element.ref).toBe(null);
223
- } else {
224
- expect(element.ref).toBe(null);
225
- }
176
+ expect(element.ref).toBe(null);
177
if (__DEV__) {
178
expect(Object.isFrozen(element)).toBe(true);
179
expect(Object.isFrozen(element.props)).toBe(true);
@@ -234,11 +185,7 @@ describe('ReactCreateElement', () => {
185
const elementA = React.createElement('div');
186
const elementB = React.createElement('div', elementA.props);
187
expect(elementB.key).toBe(null);
237
- if (gate(flags => flags.enableRefAsProp)) {
238
- expect(elementB.ref).toBe(null);
239
- } else {
240
- expect(elementB.ref).toBe(null);
241
- }
188
+ expect(elementB.ref).toBe(null);
189
});
190
191
it('coerces the key to a string', () => {
@@ -248,11 +195,7 @@ describe('ReactCreateElement', () => {
195
});
196
expect(element.type).toBe(ComponentClass);
197
expect(element.key).toBe('12');
251
- if (gate(flags => flags.enableRefAsProp)) {
252
- expect(element.ref).toBe(null);
253
- } else {
254
- expect(element.ref).toBe(null);
255
- }
198
+ expect(element.ref).toBe(null);
199
if (__DEV__) {
200
expect(Object.isFrozen(element)).toBe(true);
201
expect(Object.isFrozen(element.props)).toBe(true);
packages/react/src/__tests__/ReactElementClone-test.js
+10
-32
@@ -212,11 +212,7 @@ describe('ReactElementClone', () => {
212
ref: this.xyzRef,
213
});
214
expect(clone.key).toBe('xyz');
215
- if (gate(flags => flags.enableRefAsProp)) {
216
- expect(clone.props.ref).toBe(this.xyzRef);
217
- } else {
218
- expect(clone.ref).toBe(this.xyzRef);
219
- }
215
+ expect(clone.props.ref).toBe(this.xyzRef);
216
return <div>{clone}</div>;
217
}
218
}
@@ -274,17 +270,13 @@ describe('ReactElementClone', () => {
270
271
const root = ReactDOMClient.createRoot(document.createElement('div'));
272
await act(() => root.render(<Grandparent />));
277
- if (gate(flags => flags.enableRefAsProp && flags.disableStringRefs)) {
273
+ if (gate(flags => flags.disableStringRefs)) {
274
expect(component.childRef).toEqual({current: null});
275
expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN');
280
- } else if (
281
- gate(flags => !flags.enableRefAsProp && !flags.disableStringRefs)
282
- ) {
276
+ } else if (gate(flags => false)) {
277
expect(component.childRef).toEqual({current: null});
278
expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN');
285
- } else if (
286
- gate(flags => flags.enableRefAsProp && !flags.disableStringRefs)
287
- ) {
279
+ } else if (gate(flags => !flags.disableStringRefs)) {
280
expect(component.childRef).toEqual({current: null});
281
expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN');
282
} else {
@@ -397,11 +389,7 @@ describe('ReactElementClone', () => {
389
const elementA = React.createElement('div');
390
const elementB = React.cloneElement(elementA, elementA.props);
391
expect(elementB.key).toBe(null);
400
- if (gate(flags => flags.enableRefAsProp)) {
401
- expect(elementB.ref).toBe(null);
402
- } else {
403
- expect(elementB.ref).toBe(null);
404
- }
392
+ expect(elementB.ref).toBe(null);
393
});
394
395
it('should ignore undefined key and ref', () => {
@@ -418,21 +406,17 @@ describe('ReactElementClone', () => {
406
const clone = React.cloneElement(element, props);
407
expect(clone.type).toBe(ComponentClass);
408
expect(clone.key).toBe('12');
421
- if (gate(flags => flags.enableRefAsProp && flags.disableStringRefs)) {
409
+ if (gate(flags => flags.disableStringRefs)) {
410
expect(clone.props.ref).toBe('34');
411
expect(() => expect(clone.ref).toBe('34')).toErrorDev(
412
'Accessing element.ref was removed in React 19',
413
{withoutStack: true},
414
);
415
expect(clone.props).toEqual({foo: 'ef', ref: '34'});
428
- } else if (
429
- gate(flags => !flags.enableRefAsProp && !flags.disableStringRefs)
430
- ) {
416
+ } else if (gate(flags => false)) {
417
expect(clone.ref).toBe(element.ref);
418
expect(clone.props).toEqual({foo: 'ef'});
433
- } else if (
434
- gate(flags => flags.enableRefAsProp && !flags.disableStringRefs)
435
- ) {
419
+ } else if (gate(flags => !flags.disableStringRefs)) {
420
expect(() => {
421
expect(clone.ref).toBe(element.ref);
422
}).toErrorDev('Accessing element.ref was removed in React 19', {
@@ -462,14 +446,8 @@ describe('ReactElementClone', () => {
446
const clone = React.cloneElement(element, props);
447
expect(clone.type).toBe(ComponentClass);
448
expect(clone.key).toBe('null');
465
- if (gate(flags => flags.enableRefAsProp)) {
466
- expect(clone.ref).toBe(null);
467
- expect(clone.props).toEqual({foo: 'ef', ref: null});
468
- } else {
469
- expect(clone.ref).toBe(null);
470
- expect(clone.props).toEqual({foo: 'ef'});
471
- }
472
-
449
+ expect(clone.ref).toBe(null);
450
+ expect(clone.props).toEqual({foo: 'ef', ref: null});
451
if (__DEV__) {
452
expect(Object.isFrozen(element)).toBe(true);
453
expect(Object.isFrozen(element.props)).toBe(true);
packages/react/src/__tests__/ReactJSXElementValidator-test.js
+7
-17
@@ -248,23 +248,13 @@ describe('ReactJSXElementValidator', () => {
248
}
249
}
250
251
- if (gate(flags => flags.enableRefAsProp)) {
252
- await expect(async () => {
253
- const container = document.createElement('div');
254
- const root = ReactDOMClient.createRoot(container);
255
- await act(() => {
256
- root.render(<Foo />);
257
- });
258
- }).toErrorDev('Invalid prop `ref` supplied to `React.Fragment`.');
259
- } else {
260
- await expect(async () => {
261
- const container = document.createElement('div');
262
- const root = ReactDOMClient.createRoot(container);
263
- await act(() => {
264
- root.render(<Foo />);
265
- });
266
- }).toErrorDev('Invalid attribute `ref` supplied to `React.Fragment`.');
267
- }
251
+ await expect(async () => {
252
+ const container = document.createElement('div');
253
+ const root = ReactDOMClient.createRoot(container);
254
+ await act(() => {
255
+ root.render(<Foo />);
256
+ });
257
+ }).toErrorDev('Invalid prop `ref` supplied to `React.Fragment`.');
258
});
259
260
it('does not warn for fragments of multiple elements without keys', async () => {
packages/react/src/__tests__/ReactJSXRuntime-test.js
-29
@@ -244,34 +244,6 @@ describe('ReactJSXRuntime', () => {
244
);
245
});
246
247
- // @gate !enableRefAsProp || !__DEV__
248
- it('should warn when `ref` is being accessed', async () => {
249
- const container = document.createElement('div');
250
- class Child extends React.Component {
251
- render() {
252
- return JSXRuntime.jsx('div', {children: this.props.ref});
253
- }
254
- }
255
- class Parent extends React.Component {
256
- render() {
257
- return JSXRuntime.jsx('div', {
258
- children: JSXRuntime.jsx(Child, {ref: React.createRef()}),
259
- });
260
- }
261
- }
262
- await expect(async () => {
263
- const root = ReactDOMClient.createRoot(container);
264
- await act(() => {
265
- root.render(JSXRuntime.jsx(Parent, {}));
266
- });
267
- }).toErrorDev(
268
- 'Child: `ref` is not a prop. Trying to access it will result ' +
269
- 'in `undefined` being returned. If you need to access the same ' +
270
- 'value within the child component, you should pass it as a different ' +
271
- 'prop. (https://react.dev/link/special-props)',
272
- );
273
- });
274
-
247
it('should warn when unkeyed children are passed to jsx', async () => {
248
const container = document.createElement('div');
249
@@ -377,7 +349,6 @@ describe('ReactJSXRuntime', () => {
349
expect(didCall).toBe(false);
350
});
351
380
- // @gate enableRefAsProp
352
it('does not clone props object if key and ref is not spread', async () => {
353
const config = {
354
foo: 'foo',
packages/react/src/__tests__/ReactJSXTransformIntegration-test.js
+11
-34
@@ -55,11 +55,7 @@ describe('ReactJSXTransformIntegration', () => {
55
const element = <Component />;
56
expect(element.type).toBe(Component);
57
expect(element.key).toBe(null);
58
- if (gate(flags => flags.enableRefAsProp)) {
59
- expect(element.ref).toBe(null);
60
- } else {
61
- expect(element.ref).toBe(null);
62
- }
58
+ expect(element.ref).toBe(null);
59
const expectation = {};
60
Object.freeze(expectation);
61
expect(element.props).toEqual(expectation);
@@ -69,11 +65,7 @@ describe('ReactJSXTransformIntegration', () => {
65
const element = <div />;
66
expect(element.type).toBe('div');
67
expect(element.key).toBe(null);
72
- if (gate(flags => flags.enableRefAsProp)) {
73
- expect(element.ref).toBe(null);
74
- } else {
75
- expect(element.ref).toBe(null);
76
- }
68
+ expect(element.ref).toBe(null);
69
const expectation = {};
70
Object.freeze(expectation);
71
expect(element.props).toEqual(expectation);
@@ -84,11 +76,7 @@ describe('ReactJSXTransformIntegration', () => {
76
const element = <TagName />;
77
expect(element.type).toBe('div');
78
expect(element.key).toBe(null);
87
- if (gate(flags => flags.enableRefAsProp)) {
88
- expect(element.ref).toBe(null);
89
- } else {
90
- expect(element.ref).toBe(null);
91
- }
79
+ expect(element.ref).toBe(null);
80
const expectation = {};
81
Object.freeze(expectation);
82
expect(element.props).toEqual(expectation);
@@ -124,31 +112,20 @@ describe('ReactJSXTransformIntegration', () => {
112
const ref = React.createRef();
113
const element = <Component ref={ref} foo="56" />;
114
expect(element.type).toBe(Component);
127
- if (gate(flags => flags.enableRefAsProp)) {
128
- expect(() => expect(element.ref).toBe(ref)).toErrorDev(
129
- 'Accessing element.ref was removed in React 19',
130
- {withoutStack: true},
131
- );
132
- const expectation = {foo: '56', ref};
133
- Object.freeze(expectation);
134
- expect(element.props).toEqual(expectation);
135
- } else {
136
- const expectation = {foo: '56'};
137
- Object.freeze(expectation);
138
- expect(element.props).toEqual(expectation);
139
- expect(element.ref).toBe(ref);
140
- }
115
+ expect(() => expect(element.ref).toBe(ref)).toErrorDev(
116
+ 'Accessing element.ref was removed in React 19',
117
+ {withoutStack: true},
118
+ );
119
+ const expectation = {foo: '56', ref};
120
+ Object.freeze(expectation);
121
+ expect(element.props).toEqual(expectation);
122
});
123
124
it('coerces the key to a string', () => {
125
const element = <Component key={12} foo="56" />;
126
expect(element.type).toBe(Component);
127
expect(element.key).toBe('12');
147
- if (gate(flags => flags.enableRefAsProp)) {
148
- expect(element.ref).toBe(null);
149
- } else {
150
- expect(element.ref).toBe(null);
151
- }
128
+ expect(element.ref).toBe(null);
129
const expectation = {foo: '56'};
130
Object.freeze(expectation);
131
expect(element.props).toEqual(expectation);
packages/react/src/jsx/ReactJSXElement.js
+27
-114
@@ -20,7 +20,6 @@ import isValidElementType from 'shared/isValidElementType';
20
import isArray from 'shared/isArray';
21
import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
22
import {
23
- enableRefAsProp,
23
disableStringRefs,
24
disableDefaultPropsExceptForClasses,
25
enableOwnerStacks,
@@ -72,7 +71,6 @@ function getOwner() {
71
}
72
73
let specialPropKeyWarningShown;
75
-let specialPropRefWarningShown;
74
let didWarnAboutStringRefs;
75
let didWarnAboutElementRef;
76
let didWarnAboutOldJSXRuntime;
@@ -82,7 +80,7 @@ if (__DEV__ || enableLogStringRefsProd) {
80
didWarnAboutElementRef = {};
81
}
82
85
-const enableFastJSXWithoutStringRefs = enableRefAsProp && disableStringRefs;
83
+const enableFastJSXWithoutStringRefs = disableStringRefs;
84
85
function hasValidRef(config) {
86
if (__DEV__) {
@@ -159,30 +157,6 @@ function defineKeyPropWarningGetter(props, displayName) {
157
}
158
}
159
162
-function defineRefPropWarningGetter(props, displayName) {
163
- if (!enableRefAsProp) {
164
- if (__DEV__) {
165
- const warnAboutAccessingRef = function () {
166
- if (!specialPropRefWarningShown) {
167
- specialPropRefWarningShown = true;
168
- console.error(
169
- '%s: `ref` is not a prop. Trying to access it will result ' +
170
- 'in `undefined` being returned. If you need to access the same ' +
171
- 'value within the child component, you should pass it as a different ' +
172
- 'prop. (https://react.dev/link/special-props)',
173
- displayName,
174
- );
175
- }
176
- };
177
- warnAboutAccessingRef.isReactWarning = true;
178
- Object.defineProperty(props, 'ref', {
179
- get: warnAboutAccessingRef,
180
- configurable: true,
181
- });
182
- }
183
- }
184
-}
185
-
160
function elementRefGetterWithDeprecationWarning() {
161
if (__DEV__) {
162
const componentName = getComponentNameFromType(this.type);
@@ -225,7 +199,6 @@ function elementRefGetterWithDeprecationWarning() {
199
function ReactElement(
200
type,
201
key,
228
- _ref,
202
self,
203
source,
204
owner,
@@ -233,24 +206,18 @@ function ReactElement(
206
debugStack,
207
debugTask,
208
) {
236
- let ref;
237
- if (enableRefAsProp) {
238
- // When enableRefAsProp is on, ignore whatever was passed as the ref
239
- // argument and treat `props.ref` as the source of truth. The only thing we
240
- // use this for is `element.ref`, which will log a deprecation warning on
241
- // access. In the next release, we can remove `element.ref` as well as the
242
- // `ref` argument.
243
- const refProp = props.ref;
209
+ // Ignore whatever was passed as the ref argument and treat `props.ref` as
210
+ // the source of truth. The only thing we use this for is `element.ref`,
211
+ // which will log a deprecation warning on access. In the next release, we
212
+ // can remove `element.ref` as well as the `ref` argument.
213
+ const refProp = props.ref;
214
245
- // An undefined `element.ref` is coerced to `null` for
246
- // backwards compatibility.
247
- ref = refProp !== undefined ? refProp : null;
248
- } else {
249
- ref = _ref;
250
- }
215
+ // An undefined `element.ref` is coerced to `null` for
216
+ // backwards compatibility.
217
+ const ref = refProp !== undefined ? refProp : null;
218
219
let element;
253
- if (__DEV__ && enableRefAsProp) {
220
+ if (__DEV__) {
221
// In dev, make `ref` a non-enumerable property with a warning. It's non-
222
// enumerable so that test matchers and serializers don't access it and
223
// trigger the warning.
@@ -380,7 +347,6 @@ function ReactElement(
347
*/
348
export function jsxProd(type, config, maybeKey) {
349
let key = null;
383
- let ref = null;
350
351
// Currently, key can be spread in as a prop. This causes a potential
352
// issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
@@ -402,19 +368,9 @@ export function jsxProd(type, config, maybeKey) {
368
key = '' + config.key;
369
}
370
405
- if (hasValidRef(config)) {
406
- if (!enableRefAsProp) {
407
- ref = config.ref;
408
- if (!disableStringRefs) {
409
- ref = coerceStringRef(ref, getOwner(), type);
410
- }
411
- }
412
- }
413
-
371
let props;
372
if (
416
- (enableFastJSXWithoutStringRefs ||
417
- (enableRefAsProp && !('ref' in config))) &&
373
+ (enableFastJSXWithoutStringRefs || !('ref' in config)) &&
374
!('key' in config)
375
) {
376
// If key was not spread in, we can reuse the original props object. This
@@ -434,8 +390,8 @@ export function jsxProd(type, config, maybeKey) {
390
props = {};
391
for (const propName in config) {
392
// Skip over reserved prop names
437
- if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
438
- if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
393
+ if (propName !== 'key') {
394
+ if (!disableStringRefs && propName === 'ref') {
395
props.ref = coerceStringRef(config[propName], getOwner(), type);
396
} else {
397
props[propName] = config[propName];
@@ -459,7 +415,6 @@ export function jsxProd(type, config, maybeKey) {
415
return ReactElement(
416
type,
417
key,
462
- ref,
418
undefined,
419
undefined,
420
getOwner(),
@@ -662,7 +617,6 @@ function jsxDEVImpl(
617
}
618
619
let key = null;
665
- let ref = null;
620
621
// Currently, key can be spread in as a prop. This causes a potential
622
// issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
@@ -684,22 +638,15 @@ function jsxDEVImpl(
638
key = '' + config.key;
639
}
640
687
- if (hasValidRef(config)) {
688
- if (!enableRefAsProp) {
689
- ref = config.ref;
690
- if (!disableStringRefs) {
691
- ref = coerceStringRef(ref, getOwner(), type);
692
- }
693
- }
694
- if (!disableStringRefs) {
641
+ if (!disableStringRefs) {
642
+ if (hasValidRef(config)) {
643
warnIfStringRefCannotBeAutoConverted(config, self);
644
}
645
}
646
647
let props;
648
if (
701
- (enableFastJSXWithoutStringRefs ||
702
- (enableRefAsProp && !('ref' in config))) &&
649
+ (enableFastJSXWithoutStringRefs || !('ref' in config)) &&
650
!('key' in config)
651
) {
652
// If key was not spread in, we can reuse the original props object. This
@@ -719,8 +666,8 @@ function jsxDEVImpl(
666
props = {};
667
for (const propName in config) {
668
// Skip over reserved prop names
722
- if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
723
- if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
669
+ if (propName !== 'key') {
670
+ if (!disableStringRefs && propName === 'ref') {
671
props.ref = coerceStringRef(config[propName], getOwner(), type);
672
} else {
673
props[propName] = config[propName];
@@ -741,23 +688,17 @@ function jsxDEVImpl(
688
}
689
}
690
744
- if (key || (!enableRefAsProp && ref)) {
691
+ if (key) {
692
const displayName =
693
typeof type === 'function'
694
? type.displayName || type.name || 'Unknown'
695
: type;
749
- if (key) {
750
- defineKeyPropWarningGetter(props, displayName);
751
- }
752
- if (!enableRefAsProp && ref) {
753
- defineRefPropWarningGetter(props, displayName);
754
- }
696
+ defineKeyPropWarningGetter(props, displayName);
697
}
698
699
return ReactElement(
700
type,
701
key,
760
- ref,
702
self,
703
source,
704
getOwner(),
@@ -838,7 +779,6 @@ export function createElement(type, config, children) {
779
const props = {};
780
781
let key = null;
841
- let ref = null;
782
783
if (config != null) {
784
if (__DEV__) {
@@ -861,15 +801,8 @@ export function createElement(type, config, children) {
801
}
802
}
803
864
- if (hasValidRef(config)) {
865
- if (!enableRefAsProp) {
866
- ref = config.ref;
867
- if (!disableStringRefs) {
868
- ref = coerceStringRef(ref, getOwner(), type);
869
- }
870
- }
871
-
872
- if (__DEV__ && !disableStringRefs) {
804
+ if (__DEV__ && !disableStringRefs) {
805
+ if (hasValidRef(config)) {
806
warnIfStringRefCannotBeAutoConverted(config, config.__self);
807
}
808
}
@@ -886,7 +819,6 @@ export function createElement(type, config, children) {
819
hasOwnProperty.call(config, propName) &&
820
// Skip over reserved prop names
821
propName !== 'key' &&
889
- (enableRefAsProp || propName !== 'ref') &&
822
// Even though we don't use these anymore in the runtime, we don't want
823
// them to appear as props, so in createElement we filter them out.
824
// We don't have to do this in the jsx() runtime because the jsx()
@@ -894,7 +826,7 @@ export function createElement(type, config, children) {
826
propName !== '__self' &&
827
propName !== '__source'
828
) {
897
- if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
829
+ if (!disableStringRefs && propName === 'ref') {
830
props.ref = coerceStringRef(config[propName], getOwner(), type);
831
} else {
832
props[propName] = config[propName];
@@ -931,24 +863,18 @@ export function createElement(type, config, children) {
863
}
864
}
865
if (__DEV__) {
934
- if (key || (!enableRefAsProp && ref)) {
866
+ if (key) {
867
const displayName =
868
typeof type === 'function'
869
? type.displayName || type.name || 'Unknown'
870
: type;
939
- if (key) {
940
- defineKeyPropWarningGetter(props, displayName);
941
- }
942
- if (!enableRefAsProp && ref) {
943
- defineRefPropWarningGetter(props, displayName);
944
- }
871
+ defineKeyPropWarningGetter(props, displayName);
872
}
873
}
874
875
return ReactElement(
876
type,
877
key,
951
- ref,
878
undefined,
879
undefined,
880
getOwner(),
@@ -962,9 +888,6 @@ export function cloneAndReplaceKey(oldElement, newKey) {
888
const clonedElement = ReactElement(
889
oldElement.type,
890
newKey,
965
- // When enableRefAsProp is on, this argument is ignored. This check only
966
- // exists to avoid the `ref` access warning.
967
- enableRefAsProp ? null : oldElement.ref,
891
undefined,
892
undefined,
893
!__DEV__ && disableStringRefs ? undefined : oldElement._owner,
@@ -997,7 +920,6 @@ export function cloneElement(element, config, children) {
920
921
// Reserved names are extracted
922
let key = element.key;
1000
- let ref = enableRefAsProp ? null : element.ref;
923
924
// Owner will be preserved, unless ref is overridden
925
let owner = !__DEV__ && disableStringRefs ? undefined : element._owner;
@@ -1005,13 +927,6 @@ export function cloneElement(element, config, children) {
927
if (config != null) {
928
if (hasValidRef(config)) {
929
owner = __DEV__ || !disableStringRefs ? getOwner() : undefined;
1008
- if (!enableRefAsProp) {
1009
- // Silently steal the ref from the parent.
1010
- ref = config.ref;
1011
- if (!disableStringRefs) {
1012
- ref = coerceStringRef(ref, owner, element.type);
1013
- }
1014
- }
930
}
931
if (hasValidKey(config)) {
932
if (__DEV__) {
@@ -1034,7 +949,6 @@ export function cloneElement(element, config, children) {
949
hasOwnProperty.call(config, propName) &&
950
// Skip over reserved prop names
951
propName !== 'key' &&
1037
- (enableRefAsProp || propName !== 'ref') &&
952
// ...and maybe these, too, though we currently rely on them for
953
// warnings and debug information in dev. Need to decide if we're OK
954
// with dropping them. In the jsx() runtime it's not an issue because
@@ -1046,7 +960,7 @@ export function cloneElement(element, config, children) {
960
// Undefined `ref` is ignored by cloneElement. We treat it the same as
961
// if the property were missing. This is mostly for
962
// backwards compatibility.
1049
- !(enableRefAsProp && propName === 'ref' && config.ref === undefined)
963
+ !(propName === 'ref' && config.ref === undefined)
964
) {
965
if (
966
!disableDefaultPropsExceptForClasses &&
@@ -1056,7 +970,7 @@ export function cloneElement(element, config, children) {
970
// Resolve default props
971
props[propName] = defaultProps[propName];
972
} else {
1059
- if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
973
+ if (!disableStringRefs && propName === 'ref') {
974
props.ref = coerceStringRef(config[propName], owner, element.type);
975
} else {
976
props[propName] = config[propName];
@@ -1082,7 +996,6 @@ export function cloneElement(element, config, children) {
996
const clonedElement = ReactElement(
997
element.type,
998
key,
1085
- ref,
999
undefined,
1000
undefined,
1001
owner,
packages/shared/ReactFeatureFlags.js
+1
-6
@@ -208,13 +208,8 @@ export const enableFilterEmptyStringAttributesDOM = true;
208
// Disabled caching behavior of `react/cache` in client runtimes.
209
export const disableClientCache = true;
210
211
-// Subtle breaking changes to JSX runtime to make it faster, like passing `ref`
212
-// as a normal prop instead of stripping it from the props object.
213
-
214
-// Passes `ref` as a normal prop instead of stripping it from the props object
215
-// during element creation.
216
-export const enableRefAsProp = true;
211
export const disableStringRefs = true;
212
+
213
/**
214
* If set to a function, the function will be called with the component name
215
* and ref string.
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -73,7 +73,6 @@ export const enableProfilerCommitHooks = __PROFILE__;
73
export const enableProfilerNestedUpdatePhase = __PROFILE__;
74
export const enableProfilerTimer = __PROFILE__;
75
export const enableReactTestRendererWarning = false;
76
-export const enableRefAsProp = true;
76
export const enableRenderableContext = true;
77
export const enableRetryLaneExpiration = false;
78
export const enableSchedulingProfiler = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -63,7 +63,6 @@ export const enableOwnerStacks = false;
63
export const enablePersistedModeClonedFlag = false;
64
export const enablePostpone = false;
65
export const enableReactTestRendererWarning = false;
66
-export const enableRefAsProp = true;
66
export const enableRenderableContext = true;
67
export const enableRetryLaneExpiration = false;
68
export const enableSchedulingProfiler = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -91,7 +91,6 @@ export const enableSiblingPrerendering = false;
91
// We really need to get rid of this whole module. Any test renderer specific
92
// flags should be handled by the Fiber config.
93
// const __NEXT_MAJOR__ = __EXPERIMENTAL__;
94
-export const enableRefAsProp = true;
94
export const disableStringRefs = true;
95
export const disableLegacyMode = true;
96
export const disableLegacyContext = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js
-1
@@ -56,7 +56,6 @@ export const enableProfilerCommitHooks = __PROFILE__;
56
export const enableProfilerNestedUpdatePhase = __PROFILE__;
57
export const enableProfilerTimer = __PROFILE__;
58
export const enableReactTestRendererWarning = false;
59
-export const enableRefAsProp = true;
59
export const enableRenderableContext = true;
60
export const enableRetryLaneExpiration = false;
61
export const enableSchedulingProfiler = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -83,7 +83,6 @@ export const disableClientCache = true;
83
export const enableServerComponentLogs = true;
84
export const enableInfiniteRenderLoopDetection = false;
85
86
-export const enableRefAsProp = true;
86
export const disableStringRefs = false;
87
88
export const enableReactTestRendererWarning = false;
packages/shared/forks/ReactFeatureFlags.www.js
-2
@@ -101,8 +101,6 @@ export const enableLegacyHidden = true;
101
102
export const enableComponentStackLocations = true;
103
104
-export const enableRefAsProp = true;
105
-
104
export const disableTextareaChildren = __EXPERIMENTAL__;
105
106
export const consoleManagedByDevToolsDuringStrictMode = true;