Skip to content

Commit 98d4bf3

Browse files
committed
Fix component detection when memo and forwardRef are used together
Fixes #2349
1 parent fc70077 commit 98d4bf3

File tree

2 files changed

+93
-3
lines changed

2 files changed

+93
-3
lines changed

lib/util/Components.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,8 +422,23 @@ function componentRule(rule, context) {
422422
return utils.isReturningJSX(ASTNode, strict) || utils.isReturningNull(ASTNode);
423423
},
424424

425+
getPragmaComponentWrapper(node) {
426+
let isPragmaComponentWrapper;
427+
let currentNode = node;
428+
let prevNode;
429+
do {
430+
currentNode = currentNode.parent;
431+
isPragmaComponentWrapper = this.isPragmaComponentWrapper(currentNode);
432+
if (isPragmaComponentWrapper) {
433+
prevNode = currentNode;
434+
}
435+
} while (isPragmaComponentWrapper);
436+
437+
return prevNode;
438+
},
439+
425440
isPragmaComponentWrapper(node) {
426-
if (node.type !== 'CallExpression') {
441+
if (!node || node.type !== 'CallExpression') {
427442
return false;
428443
}
429444
const propertyNames = ['forwardRef', 'memo'];
@@ -506,8 +521,9 @@ function componentRule(rule, context) {
506521
const isArgument = node.parent && node.parent.type === 'CallExpression'; // Arguments (callback, etc.)
507522
// Attribute Expressions inside JSX Elements (<button onClick={() => props.handleClick()}></button>)
508523
const isJSXExpressionContainer = node.parent && node.parent.type === 'JSXExpressionContainer';
509-
if (isFunction && node.parent && this.isPragmaComponentWrapper(node.parent)) {
510-
return node.parent;
524+
const pragmaComponentWrapper = this.getPragmaComponentWrapper(node);
525+
if (isFunction && pragmaComponentWrapper) {
526+
return pragmaComponentWrapper;
511527
}
512528
// Stop moving up if we reach a class or an argument (like a callback)
513529
if (isClass || isArgument) {

tests/lib/rules/prop-types.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,6 +2375,43 @@ ruleTester.run('prop-types', rule, {
23752375
}
23762376
`,
23772377
parser: parsers.BABEL_ESLINT
2378+
},
2379+
{
2380+
code: `
2381+
const Label = React.memo(React.forwardRef(({ text }, ref) => {
2382+
return <div ref={ref}>{text}</div>;
2383+
}));
2384+
Label.propTypes = {
2385+
text: PropTypes.string
2386+
};
2387+
`
2388+
},
2389+
{
2390+
code: `
2391+
import React, { memo, forwardRef } from 'react';
2392+
const Label = memo(forwardRef(({ text }, ref) => {
2393+
return <div ref={ref}>{text}</div>;
2394+
}));
2395+
Label.propTypes = {
2396+
text: PropTypes.string
2397+
};
2398+
`
2399+
},
2400+
{
2401+
code: `
2402+
import Foo, { memo, forwardRef } from 'foo';
2403+
const Label = memo(forwardRef(({ text }, ref) => {
2404+
return <div ref={ref}>{text}</div>;
2405+
}));
2406+
Label.propTypes = {
2407+
text: PropTypes.string
2408+
};
2409+
`,
2410+
settings: {
2411+
react: {
2412+
pragma: 'Foo'
2413+
}
2414+
}
23782415
}
23792416
],
23802417

@@ -4723,6 +4760,43 @@ ruleTester.run('prop-types', rule, {
47234760
errors: [{
47244761
message: '\'user.age\' is missing in props validation'
47254762
}]
4763+
},
4764+
{
4765+
code: `
4766+
const Label = React.memo(React.forwardRef(({ text }, ref) => {
4767+
return <div ref={ref}>{text}</div>;
4768+
}));
4769+
`,
4770+
errors: [{
4771+
message: '\'text\' is missing in props validation'
4772+
}]
4773+
},
4774+
{
4775+
code: `
4776+
import React, { memo, forwardRef } from 'react';
4777+
const Label = memo(forwardRef(({ text }, ref) => {
4778+
return <div ref={ref}>{text}</div>;
4779+
}));
4780+
`,
4781+
errors: [{
4782+
message: '\'text\' is missing in props validation'
4783+
}]
4784+
},
4785+
{
4786+
code: `
4787+
import Foo, { memo, forwardRef } from 'foo';
4788+
const Label = memo(forwardRef(({ text }, ref) => {
4789+
return <div ref={ref}>{text}</div>;
4790+
}));
4791+
`,
4792+
settings: {
4793+
react: {
4794+
pragma: 'Foo'
4795+
}
4796+
},
4797+
errors: [{
4798+
message: '\'text\' is missing in props validation'
4799+
}]
47264800
}
47274801
]
47284802
});

0 commit comments

Comments
 (0)