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; }