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..8de6aab0038e0 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;
@@ -53,7 +47,7 @@ Component.prototype.isReactComponent = {};
* @final
* @protected
*/
-Component.prototype.setState = function (partialState, callback) {
+Component.prototype.setState = function(partialState, callback) {
if (
typeof partialState !== 'object' &&
typeof partialState !== 'function' &&
@@ -82,7 +76,7 @@ Component.prototype.setState = function (partialState, callback) {
* @final
* @protected
*/
-Component.prototype.forceUpdate = function (callback) {
+Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
@@ -104,9 +98,9 @@ if (__DEV__) {
'https://github.com/facebook/react/issues/3236).',
],
};
- const defineDeprecationWarning = function (methodName, info) {
+ const defineDeprecationWarning = function(methodName, info) {
Object.defineProperty(Component.prototype, methodName, {
- get: function () {
+ get: function() {
console.warn(
'%s(...) is deprecated in plain JavaScript React classes. %s',
info[0],
@@ -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;
}