Skip to content

Commit 0302a2f

Browse files
[Fix] prop-types: handle nested forwardRef + memo
Fixes #3521. Co-authored-by: 김상두 <[email protected]> Co-authored-by: Jordan Harband <[email protected]>
1 parent 9f4b2b9 commit 0302a2f

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
2424
* [`jsx-key`]: detect conditional returns ([#3630][] @yialo)
2525
* [`jsx-newline`]: prevent a crash when `allowMultilines ([#3633][] @ljharb)
2626
* [`no-unknown-property`]: use a better regex to avoid a crash ([#3666][] @ljharb @SCH227)
27+
* [`prop-types`]: handle nested forwardRef + memo ([#3679][] @developer-bandi)
2728

2829
### Changed
2930
* [Refactor] `propTypes`: extract type params to var ([#3634][] @HenryBrown0)
@@ -33,6 +34,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
3334
* [Docs] [`jsx-key`]: fix correct example ([#3656][] @developer-bandi)
3435
* [Tests] `jsx-wrap-multilines`: passing tests ([#3545][] @burtek)
3536

37+
[#3679]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3679
3638
[#3677]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3677
3739
[#3675]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3675
3840
[#3674]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3674

lib/rules/prop-types.js

+22-2
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ module.exports = {
154154
while (node) {
155155
const component = components.get(node);
156156

157-
const isDeclared = component && component.confidence === 2
157+
const isDeclared = component && component.confidence >= 2
158158
&& internalIsDeclaredInComponent(component.declaredPropTypes || {}, names);
159159

160160
if (isDeclared) {
@@ -176,6 +176,7 @@ module.exports = {
176176
&& !isIgnored(propType.allNames[0])
177177
&& !isDeclaredInComponent(component.node, propType.allNames)
178178
));
179+
179180
undeclareds.forEach((propType) => {
180181
report(context, messages.missingPropType, 'missingPropType', {
181182
node: propType.node,
@@ -186,13 +187,32 @@ module.exports = {
186187
});
187188
}
188189

190+
/**
191+
* @param {Object} curComponent The current component to process
192+
* @param {Object} prevComponent The prev component to process
193+
* @returns {Boolean} True if the component is nested False if not.
194+
*/
195+
function checkNestedComponent(curComponent, prevComponent) {
196+
if (curComponent.node.callee && curComponent.node.callee.name === 'memo' && prevComponent) {
197+
const prevComponentRange = prevComponent.node.range;
198+
const currentComponentRange = curComponent.node.arguments[0].range;
199+
200+
if (prevComponentRange[0] === currentComponentRange[0]
201+
&& prevComponentRange[1] === currentComponentRange[1]) {
202+
return true;
203+
}
204+
}
205+
return false;
206+
}
207+
189208
return {
190209
'Program:exit'() {
191210
const list = components.list();
192211
// Report undeclared proptypes for all classes
193212
values(list)
194213
.filter((component) => mustBeValidated(component))
195-
.forEach((component) => {
214+
.forEach((component, index, array) => {
215+
if (checkNestedComponent(component, array[index - 1])) return;
196216
reportUndeclaredPropTypes(component);
197217
});
198218
},

tests/lib/rules/prop-types.js

+43
Original file line numberDiff line numberDiff line change
@@ -4141,6 +4141,49 @@ ruleTester.run('prop-types', rule, {
41414141
};
41424142
`,
41434143
features: ['ts', 'no-babel'],
4144+
},
4145+
{
4146+
code: `
4147+
import React, { forwardRef, memo } from 'react';
4148+
4149+
interface Props1 {
4150+
age: number;
4151+
}
4152+
const HelloTemp = memo(({ age }: Props1) => {
4153+
return <div>Hello {age}</div>;
4154+
});
4155+
export const Hello = HelloTemp
4156+
`,
4157+
features: ['types'],
4158+
},
4159+
{
4160+
code: `
4161+
import React, { forwardRef, memo } from 'react';
4162+
4163+
interface Props1 {
4164+
age: number;
4165+
}
4166+
const HelloTemp = forwardRef(({ age }: Props1) => {
4167+
return <div>Hello {age}</div>;
4168+
});
4169+
export const Hello = memo(HelloTemp);
4170+
`,
4171+
features: ['types'],
4172+
},
4173+
{
4174+
code: `
4175+
import React, { forwardRef, memo } from 'react';
4176+
4177+
interface Props1 {
4178+
age: number;
4179+
}
4180+
export const Hello = memo(
4181+
forwardRef(({ age }: Props1) => {
4182+
return <div>Hello {age}</div>;
4183+
})
4184+
);
4185+
`,
4186+
features: ['types'],
41444187
}
41454188
)),
41464189

0 commit comments

Comments
 (0)