@samitouri / QOS-React / commits / 07aa494432

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;