Skip to content

Commit 46c2a18

Browse files
committed
Add no-context rule (fixes #455)
1 parent 8b567a6 commit 46c2a18

File tree

5 files changed

+484
-0
lines changed

5 files changed

+484
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
8181

8282
* [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
8383
* [react/forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
84+
* [react/no-context](docs/rules/no-context.md): Prevent usage of `context`
8485
* [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
8586
* [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods
8687
* [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`

docs/rules/no-context.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Prevent usage of context (no-context)
2+
3+
[Context](https://facebook.github.io/react/docs/context.html) is an advanced and experimental feature. The API is likely to change in future releases. Most applications will never need to use context. Especially if you are just getting started with React, you likely do not want to use context. Using context will make your code harder to understand because it makes the data flow less clear. It is similar to using global variables to pass state through your application. Consider higher order components or Flux architecture instead.
4+
5+
## Rule Details
6+
7+
The following patterns are considered warnings:
8+
9+
```jsx
10+
class Button extends React.Component {
11+
render() {
12+
return (
13+
<button style={{background: this.context.color}}>
14+
{this.props.children}
15+
</button>
16+
);
17+
}
18+
}
19+
Button.contextTypes = {
20+
color: React.PropTypes.string
21+
};
22+
```
23+
24+
```jsx
25+
const Button = (props, context) =>
26+
<button style={{background: context.color}}>
27+
{props.children}
28+
</button>;
29+
Button.contextTypes = {
30+
color: React.PropTypes.string
31+
};
32+
```
33+
34+
The following patterns are not considered warnings:
35+
36+
```jsx
37+
class Button extends React.Component {
38+
render() {
39+
return (
40+
<button style={{background: this.props.color}}>
41+
{this.props.children}
42+
</button>
43+
);
44+
}
45+
}
46+
Button.propTypes = {
47+
color: React.PropTypes.string
48+
};
49+
```
50+
51+
```jsx
52+
const Button = (props) =>
53+
<button style={{background: props.color}}>
54+
{props.children}
55+
</button>;
56+
Button.propTypes = {
57+
color: React.PropTypes.string
58+
};
59+
```

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ var rules = {
1414
'jsx-wrap-multilines': require('./lib/rules/jsx-wrap-multilines'),
1515
'self-closing-comp': require('./lib/rules/self-closing-comp'),
1616
'jsx-no-comment-textnodes': require('./lib/rules/jsx-no-comment-textnodes'),
17+
'no-context': require('./lib/rules/no-context'),
1718
'no-danger': require('./lib/rules/no-danger'),
1819
'no-set-state': require('./lib/rules/no-set-state'),
1920
'no-is-mounted': require('./lib/rules/no-is-mounted'),

lib/rules/no-context.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* @fileoverview Prevent usage of context
3+
* @author Zach Guo
4+
*/
5+
'use strict';
6+
7+
var Components = require('../util/Components');
8+
9+
// ------------------------------------------------------------------------------
10+
// Rule Definition
11+
// ------------------------------------------------------------------------------
12+
13+
module.exports = {
14+
meta: {
15+
docs: {
16+
description: 'Prevent usage of context',
17+
category: 'Best Practices',
18+
recommended: true
19+
},
20+
schema: []
21+
},
22+
23+
create: Components.detect(function(context) {
24+
25+
/**
26+
* Checks if we are using context
27+
* @param {ASTNode} node The AST node being checked.
28+
* @returns {Boolean} True if we are using context, false if not.
29+
*/
30+
function isContextUsage(node) {
31+
return Boolean(
32+
node.object.type === 'ThisExpression' &&
33+
node.property.name === 'context'
34+
);
35+
}
36+
37+
/**
38+
* Checks if we are declaring a context
39+
* @param {ASTNode} node The AST node being checked.
40+
* @returns {Boolean} True if we are declaring a context, false if not.
41+
*/
42+
function isContextTypesDeclaration(node) {
43+
// Special case for class properties
44+
// (babel-eslint does not expose property name so we have to rely on tokens)
45+
if (node && node.type === 'ClassProperty') {
46+
var tokens = context.getFirstTokens(node, 2);
47+
if (
48+
tokens[0].value === 'contextTypes' ||
49+
(tokens[1] && tokens[1].value === 'contextTypes')
50+
) {
51+
return true;
52+
}
53+
return false;
54+
}
55+
return Boolean(
56+
node &&
57+
node.name === 'contextTypes'
58+
);
59+
}
60+
61+
/**
62+
* @param {ASTNode} node We expect either an ArrowFunctionExpression,
63+
* FunctionDeclaration, or FunctionExpression
64+
* @returns {Boolean} True if we are using context, false if not.
65+
*/
66+
function isContextUsageInStatelessComponent(node) {
67+
return Boolean(
68+
node &&
69+
node.params &&
70+
node.params[1]
71+
);
72+
}
73+
74+
/**
75+
* @param {ASTNode} node We expect either an ArrowFunctionExpression,
76+
* FunctionDeclaration, or FunctionExpression
77+
*/
78+
function handleStatelessComponent(node) {
79+
if (isContextUsageInStatelessComponent(node)) {
80+
context.report({
81+
node: node,
82+
message: 'Using context is not allowed.'
83+
});
84+
}
85+
}
86+
87+
return {
88+
89+
ClassProperty: function(node) {
90+
if (isContextTypesDeclaration(node)) {
91+
context.report({
92+
node: node,
93+
message: 'Using context is not allowed.'
94+
});
95+
}
96+
},
97+
98+
MemberExpression: function(node) {
99+
if (
100+
isContextUsage(node) ||
101+
isContextTypesDeclaration(node) ||
102+
isContextTypesDeclaration(node.property)
103+
) {
104+
context.report({
105+
node: node,
106+
message: 'Using context is not allowed.'
107+
});
108+
}
109+
},
110+
111+
ObjectExpression: function(node) {
112+
node.properties.forEach(function(property) {
113+
if (!isContextTypesDeclaration(property.key)) {
114+
return;
115+
}
116+
if (property.value.type === 'ObjectExpression') {
117+
context.report({
118+
node: node,
119+
message: 'Using context is not allowed.'
120+
});
121+
}
122+
});
123+
},
124+
125+
FunctionDeclaration: handleStatelessComponent,
126+
127+
ArrowFunctionExpression: handleStatelessComponent,
128+
129+
FunctionExpression: handleStatelessComponent
130+
131+
};
132+
133+
})
134+
};

0 commit comments

Comments
 (0)