Skip to content

Commit 88f7f09

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 88f7f09

File tree

3 files changed

+66
-1
lines changed

3 files changed

+66
-1
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

+24-1
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) {
@@ -186,13 +186,36 @@ module.exports = {
186186
});
187187
}
188188

189+
/**
190+
* @param {Object} component The current component to process
191+
* @param {Array} list The all components to process
192+
* @returns {Boolean} True if the component is nested False if not.
193+
*/
194+
function checkNestedComponent(component, list) {
195+
const componentIsMemo = component.node.callee && component.node.callee.name === 'memo';
196+
const argumentIsForwardRef = component.node.arguments && component.node.arguments[0].callee && component.node.arguments[0].callee.name === 'forwardRef';
197+
if (componentIsMemo && argumentIsForwardRef) {
198+
const forwardComponent = list.find(
199+
(innerComponent) => (
200+
innerComponent.node.range[0] === component.node.arguments[0].range[0]
201+
&& innerComponent.node.range[0] === component.node.arguments[0].range[0]
202+
));
203+
204+
const isValidated = mustBeValidated(forwardComponent);
205+
const isIgnorePropsValidation = forwardComponent.ignorePropsValidation;
206+
207+
return isIgnorePropsValidation || isValidated;
208+
}
209+
}
210+
189211
return {
190212
'Program:exit'() {
191213
const list = components.list();
192214
// Report undeclared proptypes for all classes
193215
values(list)
194216
.filter((component) => mustBeValidated(component))
195217
.forEach((component) => {
218+
if (checkNestedComponent(component, values(list))) return;
196219
reportUndeclaredPropTypes(component);
197220
});
198221
},

tests/lib/rules/prop-types.js

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

0 commit comments

Comments
 (0)