Skip to content

Commit 6587485

Browse files
committed
fix no-unused-prop-types + async class properties and methods
There was an incosistency with access and storage of the usedPropTypes of a component that would cause proptypes to not be correctly marked as used if more than one prop was used in the body of an async function class property or async class method. Fixes jsx-eslint#1053 --- bug source: First time around - get the parentComponent using utils.getParentComponent() https://github.com/yannickcr/eslint-plugin-react/blob/master/lib/rules/no-unused-prop-types.js#L594 - save off the usedPropTypes array from what component was found. - modify the array with my new used props. - set the new array on the node https://github.com/yannickcr/eslint-plugin-react/blob/master/lib/rules/no-unused-prop-types.js#L638 Note that in this case the node is a MemberExpression - Components#set will then just crawl the parents of the current node (the MemberExpresion) until one was found in the internal this._list. - Because all async FunctionExpressions, ArrowFunctionExpressions, and FunctionDeclaration are treated as components of confidence 0 (not a component). The unusedPropTypes would be attached to this. (Which is usually fine). However, if the component tries to mark a prop as used again, it will read from the parentComponent using utils.getParentComponent() (which in this case will return the class) which does not have the previously used methods. The array would then be modified (created because it doesnt exist), and then set them onto the async arrow function overwriting anything that was previously there. This change just attaches used props to the found component (where the previous usedPropTypes are pulled from) if it exists, otherwise to the current node.
1 parent b646485 commit 6587485

File tree

2 files changed

+203
-1
lines changed

2 files changed

+203
-1
lines changed

lib/rules/no-unused-prop-types.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ module.exports = {
635635
break;
636636
}
637637

638-
components.set(node, {
638+
components.set(component ? component.node : node, {
639639
usedPropTypes: usedPropTypes,
640640
ignorePropsValidation: ignorePropsValidation
641641
});

tests/lib/rules/no-unused-prop-types.js

+202
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,130 @@ ruleTester.run('no-unused-prop-types', rule, {
14611461
'};'
14621462
].join('\n'),
14631463
parserOptions: parserOptions
1464+
}, {
1465+
// Props used inside of an async class property
1466+
code: [
1467+
'export class Example extends Component {',
1468+
' static propTypes = {',
1469+
' foo: PropTypes.func,',
1470+
' }',
1471+
' classProperty = async () => {',
1472+
' await this.props.foo();',
1473+
' };',
1474+
'}'
1475+
].join('\n'),
1476+
parser: 'babel-eslint'
1477+
}, {
1478+
// Multiple props used inside of an async class property
1479+
code: [
1480+
'export class Example extends Component {',
1481+
' static propTypes = {',
1482+
' foo: PropTypes.func,',
1483+
' bar: PropTypes.func,',
1484+
' baz: PropTypes.func,',
1485+
' }',
1486+
' classProperty = async () => {',
1487+
' await this.props.foo();',
1488+
' await this.props.bar();',
1489+
' await this.props.baz();',
1490+
' };',
1491+
'}'
1492+
].join('\n'),
1493+
parser: 'babel-eslint'
1494+
}, {
1495+
// Destructured props inside of async class property
1496+
code: [
1497+
'export class Example extends Component {',
1498+
' static propTypes = {',
1499+
' foo: PropTypes.func,',
1500+
' }',
1501+
' classProperty = async () => {',
1502+
' const { foo } = this.props;',
1503+
' await foo();',
1504+
' };',
1505+
'}'
1506+
].join('\n'),
1507+
parser: 'babel-eslint'
1508+
}, {
1509+
// Multiple destructured props inside of async class property
1510+
code: [
1511+
'export class Example extends Component {',
1512+
' static propTypes = {',
1513+
' foo: PropTypes.func,',
1514+
' bar: PropTypes.func,',
1515+
' baz: PropTypes.func,',
1516+
' }',
1517+
' classProperty = async () => {',
1518+
' const { foo, bar, baz } = this.props;',
1519+
' await foo();',
1520+
' await bar();',
1521+
' await baz();',
1522+
' };',
1523+
'}'
1524+
].join('\n'),
1525+
parser: 'babel-eslint'
1526+
}, {
1527+
// Props used inside of an async class method
1528+
code: [
1529+
'export class Example extends Component {',
1530+
' static propTypes = {',
1531+
' foo: PropTypes.func,',
1532+
' }',
1533+
' async method() {',
1534+
' await this.props.foo();',
1535+
' };',
1536+
'}'
1537+
].join('\n'),
1538+
parser: 'babel-eslint'
1539+
}, {
1540+
// Multiple props used inside of an async class method
1541+
code: [
1542+
'export class Example extends Component {',
1543+
' static propTypes = {',
1544+
' foo: PropTypes.func,',
1545+
' bar: PropTypes.func,',
1546+
' baz: PropTypes.func,',
1547+
' }',
1548+
' async method() {',
1549+
' await this.props.foo();',
1550+
' await this.props.bar();',
1551+
' await this.props.baz();',
1552+
' };',
1553+
'}'
1554+
].join('\n'),
1555+
parser: 'babel-eslint'
1556+
}, {
1557+
// Destrucuted props inside of async class method
1558+
code: [
1559+
'export class Example extends Component {',
1560+
' static propTypes = {',
1561+
' foo: PropTypes.func,',
1562+
' }',
1563+
' async method() {',
1564+
' const { foo } = this.props;',
1565+
' await foo();',
1566+
' };',
1567+
'}'
1568+
].join('\n'),
1569+
parser: 'babel-eslint'
1570+
}, {
1571+
// Multiple destrucuted props inside of async class method
1572+
code: [
1573+
'export class Example extends Component {',
1574+
' static propTypes = {',
1575+
' foo: PropTypes.func,',
1576+
' bar: PropTypes.func,',
1577+
' baz: PropTypes.func,',
1578+
' }',
1579+
' async method() {',
1580+
' const { foo, bar, baz } = this.props;',
1581+
' await foo();',
1582+
' await bar();',
1583+
' await baz();',
1584+
' };',
1585+
'}'
1586+
].join('\n'),
1587+
parser: 'babel-eslint'
14641588
}
14651589
],
14661590

@@ -2438,6 +2562,84 @@ ruleTester.run('no-unused-prop-types', rule, {
24382562
line: 3,
24392563
column: 16
24402564
}]
2565+
}, {
2566+
// Multiple props used inside of an async class property
2567+
code: [
2568+
'export class Example extends Component {',
2569+
' static propTypes = {',
2570+
' foo: PropTypes.func,',
2571+
' bar: PropTypes.func,',
2572+
' baz: PropTypes.func,',
2573+
' }',
2574+
' classProperty = async () => {',
2575+
' await this.props.foo();',
2576+
' await this.props.bar();',
2577+
' };',
2578+
'}'
2579+
].join('\n'),
2580+
parser: 'babel-eslint',
2581+
errors: [{
2582+
message: '\'baz\' PropType is defined but prop is never used'
2583+
}]
2584+
}, {
2585+
// Multiple destructured props inside of async class property
2586+
code: [
2587+
'export class Example extends Component {',
2588+
' static propTypes = {',
2589+
' foo: PropTypes.func,',
2590+
' bar: PropTypes.func,',
2591+
' baz: PropTypes.func,',
2592+
' }',
2593+
' classProperty = async () => {',
2594+
' const { bar, baz } = this.props;',
2595+
' await bar();',
2596+
' await baz();',
2597+
' };',
2598+
'}'
2599+
].join('\n'),
2600+
parser: 'babel-eslint',
2601+
errors: [{
2602+
message: '\'foo\' PropType is defined but prop is never used'
2603+
}]
2604+
}, {
2605+
// Multiple props used inside of an async class method
2606+
code: [
2607+
'export class Example extends Component {',
2608+
' static propTypes = {',
2609+
' foo: PropTypes.func,',
2610+
' bar: PropTypes.func,',
2611+
' baz: PropTypes.func,',
2612+
' }',
2613+
' async method() {',
2614+
' await this.props.foo();',
2615+
' await this.props.baz();',
2616+
' };',
2617+
'}'
2618+
].join('\n'),
2619+
parser: 'babel-eslint',
2620+
errors: [{
2621+
message: '\'bar\' PropType is defined but prop is never used'
2622+
}]
2623+
}, {
2624+
// Multiple destrucuted props inside of async class method
2625+
code: [
2626+
'export class Example extends Component {',
2627+
' static propTypes = {',
2628+
' foo: PropTypes.func,',
2629+
' bar: PropTypes.func,',
2630+
' baz: PropTypes.func,',
2631+
' }',
2632+
' async method() {',
2633+
' const { foo, bar } = this.props;',
2634+
' await foo();',
2635+
' await bar();',
2636+
' };',
2637+
'}'
2638+
].join('\n'),
2639+
parser: 'babel-eslint',
2640+
errors: [{
2641+
message: '\'baz\' PropType is defined but prop is never used'
2642+
}]
24412643
}/* , {
24422644
// Enable this when the following issue is fixed
24432645
// https://github.com/yannickcr/eslint-plugin-react/issues/296

0 commit comments

Comments
 (0)