diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js
index 6bf04da4b41b6..ac1b7fb3ab8e7 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.js
@@ -819,7 +819,6 @@ function mountClassInstance(
const instance = workInProgress.stateNode;
instance.props = newProps;
instance.state = workInProgress.memoizedState;
- 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;
}