Skip to content

Commit 74f99be

Browse files
ngtanljharb
authored andcommitted
[New] add no-arrow-function-lifecycle
1 parent d3b5d71 commit 74f99be

File tree

8 files changed

+855
-17
lines changed

8 files changed

+855
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,3 +2754,4 @@ If you're still not using React 15 you can keep the old behavior by setting the
27542754
[`static-property-placement`]: docs/rules/static-property-placement.md
27552755
[`jsx-curly-newline`]: docs/rules/jsx-curly-newline.md
27562756
[`jsx-no-useless-fragment`]: docs/rules/jsx-no-useless-fragment.md
2757+
[`no-arrow-function-lifecycle`]: docs/rules/no-arrow-function-lifecycle.md

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ Enable the rules that you would like to use.
109109
* [react/forbid-foreign-prop-types](docs/rules/forbid-foreign-prop-types.md): Forbid foreign propTypes
110110
* [react/no-access-state-in-setstate](docs/rules/no-access-state-in-setstate.md): Prevent using this.state inside this.setState
111111
* [react/no-array-index-key](docs/rules/no-array-index-key.md): Prevent using Array index in `key` props
112+
* [react/no-arrow-function-lifecycle](docs/rules/no-arrow-function-lifecycle.md): Don't use arrow function for lifecycle methods
112113
* [react/no-children-prop](docs/rules/no-children-prop.md): Prevent passing children as props
113114
* [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
114115
* [react/no-danger-with-children](docs/rules/no-danger-with-children.md): Prevent problem with children and props.dangerouslySetInnerHTML
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Lifecycle methods should be methods on the prototype, not class fields (react/no-arrow-function-lifecycle)
2+
3+
It is not neccessary to use arrow function for lifecycle methods. This makes things harder to test, conceptually less performant (although in practice, performance will not be affected, since most engines will optimize efficiently), and can break hot reloading patterns.
4+
5+
## Rule Details
6+
7+
The following patterns are considered warnings:
8+
9+
```jsx
10+
class Hello extends React.Component {
11+
render = () => {
12+
return <div />;
13+
}
14+
}
15+
16+
var AnotherHello = createReactClass({
17+
render: () => {
18+
return <div />;
19+
},
20+
});
21+
```
22+
23+
The following patterns are **not** considered warnings:
24+
25+
```jsx
26+
class Hello extends React.Component {
27+
render() {
28+
return <div />;
29+
}
30+
}
31+
32+
var AnotherHello = createReactClass({
33+
render() {
34+
return <div />;
35+
},
36+
});
37+
38+
```
39+
## When Not To Use It
40+
41+
If you don't care about performance of your application or conceptual correctness of class property placement, you can disable this rule.

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const allRules = {
5252
'jsx-wrap-multilines': require('./lib/rules/jsx-wrap-multilines'),
5353
'no-access-state-in-setstate': require('./lib/rules/no-access-state-in-setstate'),
5454
'no-array-index-key': require('./lib/rules/no-array-index-key'),
55+
'no-arrow-function-lifecycle': require('./lib/rules/no-arrow-function-lifecycle'),
5556
'no-children-prop': require('./lib/rules/no-children-prop'),
5657
'no-danger': require('./lib/rules/no-danger'),
5758
'no-danger-with-children': require('./lib/rules/no-danger-with-children'),
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* @fileoverview Lifecycle methods should be methods on the prototype, not class fields
3+
* @author Tan Nguyen
4+
*/
5+
'use strict';
6+
7+
const Components = require('../util/Components');
8+
const astUtil = require('../util/ast');
9+
const docsUrl = require('../util/docsUrl');
10+
const lifecycleMethods = require('../util/lifecycleMethods');
11+
12+
module.exports = {
13+
meta: {
14+
docs: {
15+
description: 'Lifecycle methods should be methods on the prototype, not class fields',
16+
category: 'Best Practices',
17+
recommended: false,
18+
url: docsUrl('no-arrow-function-lifecycle')
19+
},
20+
schema: []
21+
},
22+
23+
create: Components.detect((context, components) => {
24+
/**
25+
* @param {Array} properties list of component properties
26+
*/
27+
function reportNoArrowFunctionLifecycle(properties) {
28+
properties.forEach(node => {
29+
const propertyName = astUtil.getPropertyName(node);
30+
const nodeType = node.value && node.value.type;
31+
const isLifecycleMethod = lifecycleMethods.indexOf(propertyName) !== -1;
32+
33+
if (nodeType === 'ArrowFunctionExpression' && isLifecycleMethod) {
34+
context.report({
35+
node,
36+
message: '{{propertyName}} is a React lifecycle method, and should not be an arrow function or in a class field. Use an instance method instead.',
37+
data: {
38+
propertyName
39+
}
40+
});
41+
}
42+
});
43+
}
44+
45+
return {
46+
'Program:exit': function() {
47+
const list = components.list();
48+
Object.keys(list).forEach(component => {
49+
const properties = astUtil.getComponentProperties(list[component].node);
50+
reportNoArrowFunctionLifecycle(properties);
51+
});
52+
}
53+
};
54+
})
55+
};

lib/rules/no-typos.js

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,13 @@
77
const PROP_TYPES = Object.keys(require('prop-types'));
88
const Components = require('../util/Components');
99
const docsUrl = require('../util/docsUrl');
10+
const lifecycleMethods = require('../util/lifecycleMethods');
1011

1112
// ------------------------------------------------------------------------------
1213
// Rule Definition
1314
// ------------------------------------------------------------------------------
1415

1516
const STATIC_CLASS_PROPERTIES = ['propTypes', 'contextTypes', 'childContextTypes', 'defaultProps'];
16-
const LIFECYCLE_METHODS = [
17-
'getDerivedStateFromProps',
18-
'componentWillMount',
19-
'UNSAFE_componentWillMount',
20-
'componentDidMount',
21-
'componentWillReceiveProps',
22-
'UNSAFE_componentWillReceiveProps',
23-
'shouldComponentUpdate',
24-
'componentWillUpdate',
25-
'UNSAFE_componentWillUpdate',
26-
'getSnapshotBeforeUpdate',
27-
'componentDidUpdate',
28-
'componentDidCatch',
29-
'componentWillUnmount',
30-
'render'
31-
];
3217

3318
module.exports = {
3419
meta: {
@@ -143,7 +128,7 @@ module.exports = {
143128
}
144129

145130
function reportErrorIfLifecycleMethodCasingTypo(node) {
146-
LIFECYCLE_METHODS.forEach((method) => {
131+
lifecycleMethods.forEach(method => {
147132
if (method.toLowerCase() === node.key.name.toLowerCase() && method !== node.key.name) {
148133
context.report({
149134
node,

lib/util/lifecycleMethods.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @fileoverview lifecycle methods
3+
* @author Tan Nguyen
4+
*/
5+
'use strict';
6+
7+
module.exports = [
8+
'getDefaultProps',
9+
'getInitialState',
10+
'getChildContext',
11+
'getDerivedStateFromProps',
12+
'componentWillMount',
13+
'UNSAFE_componentWillMount',
14+
'componentDidMount',
15+
'componentWillReceiveProps',
16+
'UNSAFE_componentWillReceiveProps',
17+
'shouldComponentUpdate',
18+
'componentWillUpdate',
19+
'UNSAFE_componentWillUpdate',
20+
'getSnapshotBeforeUpdate',
21+
'componentDidUpdate',
22+
'componentDidCatch',
23+
'componentWillUnmount',
24+
'render'
25+
];

0 commit comments

Comments
 (0)