Fix resolution of outer props with React.memo() (#14312)

* Add failing test for defaultProps between lazy() and memo()

* Add another regression test for defaultProps resolution order

* Resolve outer props for MemoComponent
This commit is contained in:
Dan Abramov 2018-11-22 19:40:42 +00:00 committed by GitHub
parent 14be29b2b9
commit 0c7189d923
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 137 additions and 2 deletions

View File

@ -250,7 +250,12 @@ function updateMemoComponent(
): null | Fiber {
if (current === null) {
let type = Component.type;
if (isSimpleFunctionComponent(type) && Component.compare === null) {
if (
isSimpleFunctionComponent(type) &&
Component.compare === null &&
// SimpleMemoComponent codepath doesn't resolve outer props either.
Component.defaultProps === undefined
) {
// If this is a plain function component without default props,
// and with only the default shallow comparison, we upgrade it
// to a SimpleMemoComponent to allow fast path updates.
@ -1739,7 +1744,9 @@ function beginWork(
case MemoComponent: {
const type = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps = resolveDefaultProps(type.type, unresolvedProps);
// Resolve outer props first, then resolve inner props.
let resolvedProps = resolveDefaultProps(type, unresolvedProps);
resolvedProps = resolveDefaultProps(type.type, resolvedProps);
return updateMemoComponent(
current,
workInProgress,

View File

@ -533,4 +533,132 @@ describe('ReactLazy', () => {
expect(root).toMatchRenderedOutput('FooBar');
expect(ref.current).not.toBe(null);
});
// Regression test for #14310
it('supports defaultProps defined on the memo() return value', async () => {
const Add = React.memo(props => {
return props.inner + props.outer;
});
Add.defaultProps = {
inner: 2,
};
const LazyAdd = lazy(() => fakeImport(Add));
const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd outer={2} />
</Suspense>,
{
unstable_isConcurrent: true,
},
);
expect(root).toFlushAndYield(['Loading...']);
expect(root).toMatchRenderedOutput(null);
// Mount
await Promise.resolve();
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('4');
// Update (shallowly equal)
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd outer={2} />
</Suspense>,
);
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('4');
// Update
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd outer={3} />
</Suspense>,
);
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('5');
// Update (shallowly equal)
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd outer={3} />
</Suspense>,
);
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('5');
// Update (explicit props)
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd outer={1} inner={1} />
</Suspense>,
);
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('2');
// Update (explicit props, shallowly equal)
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd outer={1} inner={1} />
</Suspense>,
);
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('2');
// Update
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd outer={1} />
</Suspense>,
);
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('3');
});
it('merges defaultProps in the correct order', async () => {
let Add = React.memo(props => {
return props.inner + props.outer;
});
Add.defaultProps = {
inner: 100,
};
Add = React.memo(Add);
Add.defaultProps = {
inner: 2,
outer: 0,
};
const LazyAdd = lazy(() => fakeImport(Add));
const root = ReactTestRenderer.create(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd outer={2} />
</Suspense>,
{
unstable_isConcurrent: true,
},
);
expect(root).toFlushAndYield(['Loading...']);
expect(root).toMatchRenderedOutput(null);
// Mount
await Promise.resolve();
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('4');
// Update
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd outer={3} />
</Suspense>,
);
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('5');
// Update
root.update(
<Suspense fallback={<Text text="Loading..." />}>
<LazyAdd />
</Suspense>,
);
root.unstable_flushAll();
expect(root).toMatchRenderedOutput('2');
});
});