diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 8f1e8d971a..23df1d25bd 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -951,7 +951,7 @@ function mountLazyComponent( null, workInProgress, Component, - resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too, + resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too updateExpirationTime, renderExpirationTime, ); diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index 52d2327d14..6efd405aba 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -632,6 +632,8 @@ describe('ReactLazy', () => { expect(root).toMatchRenderedOutput('0'); } + // Note: all "with defaultProps" tests below also verify defaultProps works as expected. + // If we ever delete or move propTypes-related tests, make sure not to delete these. it('respects propTypes on function component with defaultProps', async () => { function Add(props) { expect(props.innerWithDefault).toBe(42); @@ -767,6 +769,54 @@ describe('ReactLazy', () => { await verifyInnerPropTypesAreChecked(React.memo(Add)); }); + it('uses outer resolved props for validating propTypes on memo', async () => { + let T = props => { + return ; + }; + T.defaultProps = { + text: 'Inner default text', + }; + T = React.memo(T); + T.propTypes = { + // Should not be satisfied by the *inner* defaultProps. + text: PropTypes.string.isRequired, + }; + const LazyText = lazy(() => fakeImport(T)); + const root = ReactTestRenderer.create( + }> + + , + { + unstable_isConcurrent: true, + }, + ); + + expect(root).toFlushAndYield(['Loading...']); + expect(root).toMatchRenderedOutput(null); + + // Mount + await Promise.resolve(); + expect(() => { + root.unstable_flushAll(); + }).toWarnDev( + 'The prop `text` is marked as required in `T`, but its value is `undefined`', + ); + expect(root).toMatchRenderedOutput('Inner default text'); + + // Update + root.update( + }> + + , + ); + expect(() => { + root.unstable_flushAll(); + }).toWarnDev( + 'The prop `text` is marked as required in `T`, but its value is `null`', + ); + expect(root).toMatchRenderedOutput(null); + }); + it('includes lazy-loaded component in warning stack', async () => { const LazyFoo = lazy(() => { ReactTestRenderer.unstable_yield('Started loading');