From b039be627dd7403d7d2f63a48c8d263d955ce456 Mon Sep 17 00:00:00 2001 From: Jan Kassens Date: Wed, 24 Apr 2024 10:03:09 -0400 Subject: [PATCH] Unrevert "Support writing to this.refs from userspace" (#28879) Reverts facebook/react#28877 We found the cause of the regression and should be able to land this again. --- .../src/ReactFiberClassComponent.js | 12 +++++++--- .../src/__tests__/ReactFiberRefs-test.js | 24 +++++++++++++++++++ packages/react/src/ReactBaseClasses.js | 10 ++------ 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 6bf04da4b41b6..037749f836d76 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -19,12 +19,13 @@ import { } from './ReactFiberFlags'; import { debugRenderPhaseSideEffectsForStrictMode, + disableDefaultPropsExceptForClasses, disableLegacyContext, + disableStringRefs, enableDebugTracing, - enableSchedulingProfiler, enableLazyContextPropagation, enableRefAsProp, - disableDefaultPropsExceptForClasses, + enableSchedulingProfiler, } from 'shared/ReactFeatureFlags'; import ReactStrictModeWarnings from './ReactStrictModeWarnings'; import {isMounted} from './ReactFiberTreeReflection'; @@ -819,7 +820,12 @@ function mountClassInstance( const instance = workInProgress.stateNode; instance.props = newProps; instance.state = workInProgress.memoizedState; - instance.refs = {}; + if (!disableStringRefs) { + // When string refs are used in create-react-class legacy components, + // we need to make refs writable unless we patch all such copies of the + // class code that sets to a frozen emptyObject. + instance.refs = {}; + } initializeUpdateQueue(workInProgress); diff --git a/packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js b/packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js index e46387d8cc7dd..175c849d94710 100644 --- a/packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js +++ b/packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js @@ -138,4 +138,28 @@ describe('ReactFiberRefs', () => { ); expect(refProp).toBe('child'); }); + + test('strings refs can be codemodded to callback refs', async () => { + let app; + class App extends React.Component { + render() { + app = this; + return ( +
{ + // `refs` used to be a shared frozen object unless/until a string + // ref attached by the reconciler, but it's not anymore so that we + // can codemod string refs to userspace callback refs. + this.refs.div = el; + }} + /> + ); + } + } + + const root = ReactNoop.createRoot(); + await act(() => root.render()); + expect(app.refs.div.prop).toBe('Hello!'); + }); }); diff --git a/packages/react/src/ReactBaseClasses.js b/packages/react/src/ReactBaseClasses.js index 7895a97e3a1ef..ce81071937574 100644 --- a/packages/react/src/ReactBaseClasses.js +++ b/packages/react/src/ReactBaseClasses.js @@ -8,19 +8,13 @@ import ReactNoopUpdateQueue from './ReactNoopUpdateQueue'; import assign from 'shared/assign'; -const emptyObject = {}; -if (__DEV__) { - Object.freeze(emptyObject); -} - /** * Base class helpers for the updating state of a component. */ function Component(props, context, updater) { this.props = props; this.context = context; - // If a component has string refs, we will assign a different object later. - this.refs = emptyObject; + this.refs = {}; // We initialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; @@ -133,7 +127,7 @@ function PureComponent(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. - this.refs = emptyObject; + this.refs = {}; this.updater = updater || ReactNoopUpdateQueue; }