Change:
<!-- Note: if the issue is about documentation or the website, please file it at: --Do you want to request a feature or report a bug? Bug What is the current behavior? Attempting to assign a ref to a component wrapped in does not produce a friendly error message. Attempting the same thing with a functional component not wrapped in memo gives a descriptive error ("Warning: Function components cannot be given refs. Attempts to access this ref will fail."). If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle () or CodeSandbox () example below: I trust this is not needed for this case What is the expected behavior? The same error message as with a non-memo functional component should be displayed. Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React? 0

Discussion:
// to a SimpleMemoComponent to allow fast path updates. workInProgress.tag = SimpleMemoComponent; workInProgress.type = type; <ins> if (__DEV__) { validateFunctionComponentInDev(workInProgress, type); } </ins> return updateSimpleMemoComponent( current, workInProgress,

// Proceed under the assumption that this is a function component workInProgress.tag = FunctionComponent; value = finishHooks(Component, props, value, context); <ins> reconcileChildren(null, workInProgress, value, renderExpirationTime); </ins> if (__DEV__) { <del> if (Component) { warningWithoutStack( !Component.childContextTypes, '%s(...): childContextTypes cannot be defined on a function component.', Component.displayName || Component.name || 'Component', ); } if (workInProgress.ref !== null) { let info = ''; const ownerName = ReactCurrentFiber.getCurrentFiberOwnerNameInDevOrNull(); if (ownerName) { info += 'nnCheck the render method of `' + ownerName + '`.'; } </del> <ins> validateFunctionComponentInDev(workInProgress, Component); } return workInProgress.child; } } </ins> <del> let warningKey = ownerName || workInProgress._debugID || ''; const debugSource = workInProgress._debugSource; if (debugSource) { warningKey = debugSource.fileName + ':' + debugSource.lineNumber; } if (!didWarnAboutFunctionRefs[warningKey]) { didWarnAboutFunctionRefs[warningKey] = true; warning( false, 'Function components cannot be given refs. ' + 'Attempts to access this ref will fail.%s', info, ); } } </del> <ins> function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) { if (Component) { warningWithoutStack( !Component.childContextTypes, '%s(...): childContextTypes cannot be defined on a function component.', Component.displayName || Component.name || 'Component', ); } if (workInProgress.ref !== null) { let info = ''; const ownerName = ReactCurrentFiber.getCurrentFiberOwnerNameInDevOrNull(); if (ownerName) { info += 'nnCheck the render method of `' + ownerName + '`.'; } let warningKey = ownerName || workInProgress._debugID || ''; const debugSource = workInProgress._debugSource; if (debugSource) { warningKey = debugSource.fileName + ':' + debugSource.lineNumber; } if (!didWarnAboutFunctionRefs[warningKey]) { didWarnAboutFunctionRefs[warningKey] = true; warning( false, 'Function components cannot be given refs. ' + 'Attempts to access this ref will fail.%s', info, ); } } </ins> <del> if (typeof Component.getDerivedStateFromProps === 'function') { const componentName = getComponentName(Component) || 'Unknown'; </del> <ins> if (typeof Component.getDerivedStateFromProps === 'function') { const componentName = getComponentName(Component) || 'Unknown'; </ins> <del> if (!didWarnAboutGetDerivedStateOnFunctionComponent[componentName]) { warningWithoutStack( false, '%s: Function components do not support getDerivedStateFromProps.', componentName, ); didWarnAboutGetDerivedStateOnFunctionComponent[componentName] = true; } } </del> <ins> if (!didWarnAboutGetDerivedStateOnFunctionComponent[componentName]) { warningWithoutStack( false, '%s: Function components do not support getDerivedStateFromProps.', componentName, ); didWarnAboutGetDerivedStateOnFunctionComponent[componentName] = true; } } </ins> <del> if ( typeof Component.contextType === 'object' && Component.contextType !== null ) { const componentName = getComponentName(Component) || 'Unknown'; </del> <ins> if ( typeof Component.contextType === 'object' && Component.contextType !== null ) { const componentName = getComponentName(Component) || 'Unknown'; </ins> <del> if (!didWarnAboutContextTypeOnFunctionComponent[componentName]) { warningWithoutStack( false, '%s: Function components do not support contextType.', componentName, ); didWarnAboutContextTypeOnFunctionComponent[componentName] = true; } } </del> <ins> if (!didWarnAboutContextTypeOnFunctionComponent[componentName]) { warningWithoutStack( false, '%s: Function components do not support contextType.', componentName, ); didWarnAboutContextTypeOnFunctionComponent[componentName] = true; </ins> } <del> reconcileChildren(null, workInProgress, value, renderExpirationTime); return workInProgress.child; </del> } }

let React; let ReactFeatureFlags; let ReactNoop; <ins> let Suspense; </ins> describe('memo', () => { beforeEach(() => {

ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false; React = require('react'); ReactNoop = require('react-noop-renderer'); <ins> ({Suspense} = React); </ins> }); function span(prop) {

return {default: result}; } <ins> it('warns when giving a ref (simple)', async () => { // This test lives outside sharedTests because the wrappers don't forward // refs properly, and they end up affecting the current owner which is used // by the warning (making the messages not line up). function App() { return null; } App = React.memo(App); function Outer() { return <App ref={() => {}} />; } ReactNoop.render(<Outer />); expect(ReactNoop.flush).toWarnDev([ 'Warning: Function components cannot be given refs. Attempts to access ' + 'this ref will fail.', ]); }); it('warns when giving a ref (complex)', async () => { // defaultProps means this won't use SimpleMemoComponent (as of this writing) // SimpleMemoComponent is unobservable tho, so we can't check :) function App() { return null; } App.defaultProps = {}; App = React.memo(App); function Outer() { return <App ref={() => {}} />; } ReactNoop.render(<Outer />); expect(ReactNoop.flush).toWarnDev([ 'Warning: Function components cannot be given refs. Attempts to access ' + 'this ref will fail.', ]); }); </ins> // Tests should run against both the lazy and non-lazy versions of `memo`. // To make the tests work for both versions, we wrap the non-lazy version in // a lazy function component.

function sharedTests(label, memo) { describe(`${label}`, () => { it('bails out on props equality', async () => { <del> const {Suspense} = React; </del> function Counter({count}) { return <Text text={count} />; }

}); it("does not bail out if there's a context change", async () => { <del> const {Suspense} = React; </del> const CountContext = React.createContext(0); function readContext(Context) {

}); it('accepts custom comparison function', async () => { <del> const {Suspense} = React; </del> function Counter({count}) { return <Text text={count} />; }

}); it('supports non-pure class components', async () => { <del> const {Suspense} = React; </del> class CounterInner extends React.Component { static defaultProps = {suffix: '!'}; render() {