Skip to content

Commit 9eb81bc

Browse files
YONGJAE LEEljharb
authored andcommitted
[New] jsx-handler-names: add checkInlineFunction option
1 parent 20103c5 commit 9eb81bc

File tree

4 files changed

+89
-6
lines changed

4 files changed

+89
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
77

88
### Added
99
* [`button-has-type`]: support trivial ternary expressions ([#2748][] @Hypnosphi)
10+
* [`jsx-handler-names`]: add `checkInlineFunction` option ([#2761][] @dididy)
1011

1112
### Fixed
1213
* [`function-component-definition`]: ignore object properties ([#2771][] @stefan-wullems)
1314

1415
[#2771]: https://github.com/yannickcr/eslint-plugin-react/pull/2771
16+
[#2761]: https://github.com/yannickcr/eslint-plugin-react/pull/2761
1517
[#2748]: https://github.com/yannickcr/eslint-plugin-react/pull/2748
1618

1719
## [7.20.6] - 2020.08.12

docs/rules/jsx-handler-names.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,16 @@ The following patterns are **not** considered warnings:
3131
"react/jsx-handler-names": [<enabled>, {
3232
"eventHandlerPrefix": <eventHandlerPrefix>,
3333
"eventHandlerPropPrefix": <eventHandlerPropPrefix>,
34-
"checkLocalVariables": <boolean>
34+
"checkLocalVariables": <boolean>,
35+
"checkInlineFunction": <boolean>
3536
}]
3637
...
3738
```
3839

3940
* `eventHandlerPrefix`: Prefix for component methods used as event handlers. Defaults to `handle`
4041
* `eventHandlerPropPrefix`: Prefix for props that are used as event handlers. Defaults to `on`
4142
* `checkLocalVariables`: Determines whether event handlers stored as local variables are checked. Defaults to `false`
43+
* `checkInlineFunction`: Determines whether event handlers set as inline functions are checked. Defaults to `false`
4244

4345
## When Not To Use It
4446

lib/rules/jsx-handler-names.js

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ module.exports = {
2727
properties: {
2828
eventHandlerPrefix: {type: 'string'},
2929
eventHandlerPropPrefix: {type: 'string'},
30-
checkLocalVariables: {type: 'boolean'}
30+
checkLocalVariables: {type: 'boolean'},
31+
checkInlineFunction: {type: 'boolean'}
3132
},
3233
additionalProperties: false
3334
}, {
@@ -38,7 +39,8 @@ module.exports = {
3839
type: 'boolean',
3940
enum: [false]
4041
},
41-
checkLocalVariables: {type: 'boolean'}
42+
checkLocalVariables: {type: 'boolean'},
43+
checkInlineFunction: {type: 'boolean'}
4244
},
4345
additionalProperties: false
4446
}, {
@@ -49,7 +51,8 @@ module.exports = {
4951
enum: [false]
5052
},
5153
eventHandlerPropPrefix: {type: 'string'},
52-
checkLocalVariables: {type: 'boolean'}
54+
checkLocalVariables: {type: 'boolean'},
55+
checkInlineFunction: {type: 'boolean'}
5356
},
5457
additionalProperties: false
5558
}, {
@@ -58,6 +61,12 @@ module.exports = {
5861
checkLocalVariables: {type: 'boolean'}
5962
},
6063
additionalProperties: false
64+
}, {
65+
type: 'object',
66+
properties: {
67+
checkInlineFunction: {type: 'boolean'}
68+
},
69+
additionalProperties: false
6170
}
6271
]
6372
}]
@@ -68,6 +77,10 @@ module.exports = {
6877
return prefix === false;
6978
}
7079

80+
function isInlineHandler(node) {
81+
return node.value.expression.type === 'ArrowFunctionExpression';
82+
}
83+
7184
const configuration = context.options[0] || {};
7285

7386
const eventHandlerPrefix = isPrefixDisabled(configuration.eventHandlerPrefix)
@@ -86,14 +99,29 @@ module.exports = {
8699

87100
const checkLocal = !!configuration.checkLocalVariables;
88101

102+
const checkInlineFunction = !!configuration.checkInlineFunction;
103+
89104
return {
90105
JSXAttribute(node) {
91-
if (!node.value || !node.value.expression || (!checkLocal && !node.value.expression.object)) {
106+
if (
107+
!node.value
108+
|| !node.value.expression
109+
|| (
110+
!checkLocal
111+
&& (isInlineHandler(node)
112+
? !node.value.expression.body.callee.object
113+
: !node.value.expression.object
114+
)
115+
)
116+
) {
92117
return;
93118
}
94119

95120
const propKey = typeof node.name === 'object' ? node.name.name : node.name;
96-
const propValue = context.getSourceCode().getText(node.value.expression).replace(/^this\.|.*::/, '');
121+
const expression = node.value.expression;
122+
const propValue = context.getSourceCode()
123+
.getText(checkInlineFunction && isInlineHandler(node) ? expression.body.callee : expression)
124+
.replace(/^this\.|.*::/, '');
97125

98126
if (propKey === 'ref') {
99127
return;

tests/lib/rules/jsx-handler-names.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ ruleTester.run('jsx-handler-names', rule, {
4242
options: [{
4343
checkLocalVariables: false
4444
}]
45+
}, {
46+
code: '<TestComponent onChange={() => handleChange()} />',
47+
options: [{
48+
checkInlineFunction: true,
49+
checkLocalVariables: true
50+
}]
51+
}, {
52+
code: '<TestComponent onChange={() => this.handleChange()} />',
53+
options: [{
54+
checkInlineFunction: true
55+
}]
4556
}, {
4657
code: '<TestComponent onChange={this.props.onFoo} />'
4758
}, {
@@ -71,6 +82,24 @@ ruleTester.run('jsx-handler-names', rule, {
7182
}, {
7283
code: '<TestComponent onChange={props.foo::handleChange} />',
7384
parser: parsers.BABEL_ESLINT
85+
}, {
86+
code: '<TestComponent onChange={() => props::handleChange()} />',
87+
parser: parsers.BABEL_ESLINT,
88+
options: [{
89+
checkInlineFunction: true
90+
}]
91+
}, {
92+
code: '<TestComponent onChange={() => ::props.onChange()} />',
93+
parser: parsers.BABEL_ESLINT,
94+
options: [{
95+
checkInlineFunction: true
96+
}]
97+
}, {
98+
code: '<TestComponent onChange={() => props.foo::handleChange()} />',
99+
parser: parsers.BABEL_ESLINT,
100+
options: [{
101+
checkInlineFunction: true
102+
}]
74103
}, {
75104
code: '<TestComponent only={this.only} />'
76105
}, {
@@ -115,6 +144,12 @@ ruleTester.run('jsx-handler-names', rule, {
115144
options: [{
116145
checkLocalVariables: true
117146
}]
147+
}, {
148+
code: '<TestComponent onChange={() => this.takeCareOfChange()} />',
149+
errors: [{message: 'Handler function for onChange prop key must begin with \'handle\''}],
150+
options: [{
151+
checkInlineFunction: true
152+
}]
118153
}, {
119154
code: '<TestComponent only={this.handleChange} />',
120155
errors: [{message: 'Prop key for handleChange must begin with \'on\''}]
@@ -127,6 +162,13 @@ ruleTester.run('jsx-handler-names', rule, {
127162
options: [{
128163
checkLocalVariables: true
129164
}]
165+
}, {
166+
code: '<TestComponent whenChange={() => handleChange()} />',
167+
errors: [{message: 'Prop key for handleChange must begin with \'on\''}],
168+
options: [{
169+
checkInlineFunction: true,
170+
checkLocalVariables: true
171+
}]
130172
}, {
131173
code: '<TestComponent onChange={handleChange} />',
132174
errors: [{message: 'Prop key for handleChange must begin with \'when\''}],
@@ -135,6 +177,15 @@ ruleTester.run('jsx-handler-names', rule, {
135177
eventHandlerPrefix: 'handle',
136178
eventHandlerPropPrefix: 'when'
137179
}]
180+
}, {
181+
code: '<TestComponent onChange={() => handleChange()} />',
182+
errors: [{message: 'Prop key for handleChange must begin with \'when\''}],
183+
options: [{
184+
checkInlineFunction: true,
185+
checkLocalVariables: true,
186+
eventHandlerPrefix: 'handle',
187+
eventHandlerPropPrefix: 'when'
188+
}]
138189
}, {
139190
code: '<TestComponent onChange={this.onChange} />',
140191
errors: [{message: 'Handler function for onChange prop key must begin with \'handle\''}]

0 commit comments

Comments
 (0)