Skip to content

Commit 6b447b1

Browse files
committed
fix: including chnages for MDTP-prefer-object to MDTP-returns-object
1 parent 724f54f commit 6b447b1

File tree

6 files changed

+156
-79
lines changed

6 files changed

+156
-79
lines changed

docs/rules/mapDispatchToProps-prefer-object.md

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Enforces that mapDispatchToProps returns an object. (react-redux/mapDispatchToProps-returns-object)
2+
3+
Enforces that the mapDispatchToProps is an object or a function returning an object.
4+
5+
*Note: All of the caught cases would have caused a runtime [warning](https://github.com/reactjs/react-redux/blob/master/src/utils/verifyPlainObject.js) by react-redux *
6+
7+
## Rule details
8+
9+
The following pattern is considered incorrect:
10+
11+
```js
12+
const mapDispatchToProps = (dispatch) => dispatch(action())
13+
```
14+
15+
```js
16+
connect((state) => state, (dispatch) => dispatch(action()))(App)
17+
```
18+
19+
```js
20+
const mapDispatchToProps = () => {}
21+
```
22+
23+
The following patterns are considered correct:
24+
25+
26+
```js
27+
const mapDispatchToProps = {}
28+
```
29+
30+
```js
31+
const mapDispatchToProps = null;
32+
```
33+
34+
```js
35+
const mapDispatchToProps = {anAction: anAction}
36+
```
37+
38+
```js
39+
const mapDispatchToProps = (dispatch) => ({anAction: dispatch(anAction())})
40+
```
41+
42+
## Not supported use cases.
43+
44+
#### mapDispatchToProps is a function but actions are not bound to dispatch
45+
46+
>If a function is passed, it will be given dispatch as the first parameter. It’s up to you to return an object that somehow uses dispatch to bind action creators in your own way.
47+
48+
Below use case is likely not what you want but will not be enforced by this rule nor runtime react-redux check:
49+
50+
```js
51+
const mapDispatchToProps = () => ({
52+
action
53+
});
54+
```
55+
56+
In this scenario action wouldn't be wrapped in dispatch and thus wouldn't be triggered.
57+
58+
Most likely it needs to be rewritten as:
59+
60+
```js
61+
const mapDispatchToProps = {
62+
action
63+
};
64+
```
65+
or
66+
67+
```js
68+
const mapDispatchToProps = (dispatch) => ({
69+
action: () => dispatch(action())
70+
});
71+
```
72+
73+
or
74+
75+
```js
76+
const mapDispatchToProps = (dispatch) => ({
77+
action: () => bindActionCreators(action, dispatch)
78+
});
79+
```
80+
81+
#### mapDispatchToProps is equal to or returns a variable
82+
83+
Note that if mapDispatchToProps is assigned a value of a variable there is no way for lint to know if the variable resolves to an object.
84+
85+
So both of below use cases will be considered correct by the rule event though the second one is technically incorrect.
86+
87+
This one would be caught by a react-redux check.
88+
89+
```js
90+
const actionsMap = {
91+
action1,
92+
action2,
93+
};
94+
const mapDispatchToProps = actionsMap;
95+
```
96+
97+
```js
98+
const mapDispatchToProps = aSingleAction;
99+
```

index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const rules = {
22
'connect-prefer-minimum-two-arguments': require('./lib/rules/connect-prefer-minimum-two-arguments'),
33
'connect-prefer-named-arguments': require('./lib/rules/connect-prefer-named-arguments'),
4-
'mapDispatchToProps-prefer-object': require('./lib/rules/mapDispatchToProps-prefer-object'),
4+
'mapDispatchToProps-returns-object': require('./lib/rules/mapDispatchToProps-returns-object'),
55
'mapDispatchToProps-prefer-parameters-names': require('./lib/rules/mapDispatchToProps-prefer-parameters-names'),
66
'mapStateToProps-no-store': require('./lib/rules/mapStateToProps-no-store'),
77
'mapStateToProps-prefer-parameters-names': require('./lib/rules/mapStateToProps-prefer-parameters-names'),
@@ -27,7 +27,7 @@ module.exports = {
2727
'react-redux/connect-prefer-minimum-two-arguments': 0,
2828
'react-redux/connect-prefer-named-arguments': 2,
2929
'react-redux/mapDispatchToProps-prefer-parameters-names': 2,
30-
'react-redux/mapDispatchToProps-prefer-object': 2,
30+
'react-redux/mapDispatchToProps-returns-object': 2,
3131
'react-redux/mapStateToProps-no-store': 2,
3232
'react-redux/mapStateToProps-prefer-parameters-names': 2,
3333
'react-redux/prefer-separate-component-file': 1,

lib/rules/mapDispatchToProps-prefer-object.js renamed to lib/rules/mapDispatchToProps-returns-object.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const isReactReduxConnect = require('../isReactReduxConnect');
2+
const utils = require('../utils');
23

34
const report = function (context, node) {
45
context.report({
@@ -17,14 +18,20 @@ module.exports = function (context) {
1718
decl.init.type === 'ArrowFunctionExpression' ||
1819
decl.init.type === 'FunctionExpression'
1920
)) {
20-
report(context, decl);
21+
const returnNode = utils.getReturnNode(decl.init);
22+
if (!utils.isObject(returnNode)) {
23+
report(context, node);
24+
}
2125
}
2226
}
2327
});
2428
},
2529
FunctionDeclaration(node) {
2630
if (node.id && node.id.name === 'mapDispatchToProps') {
27-
report(context, node.body);
31+
const returnNode = utils.getReturnNode(node.body);
32+
if (!utils.isObject(returnNode)) {
33+
report(context, node);
34+
}
2835
}
2936
},
3037
CallExpression(node) {
@@ -34,7 +41,10 @@ module.exports = function (context) {
3441
mapDispatchToProps.type === 'ArrowFunctionExpression' ||
3542
mapDispatchToProps.type === 'FunctionExpression')
3643
) {
37-
report(context, mapDispatchToProps);
44+
const returnNode = utils.getReturnNode(mapDispatchToProps);
45+
if (!utils.isObject(returnNode)) {
46+
report(context, node);
47+
}
3848
}
3949
}
4050
},

lib/utils.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
'use strict';
22

3+
const isObject = node => node && (
4+
node.type === 'ObjectExpression' || node.type === 'Identifier'
5+
);
6+
37
const getReturnNode = (node) => {
48
const body = node.body;
5-
if (!body || !body.length) {
9+
if (!body) {
610
return node;
11+
} else if (isObject(body)) {
12+
return body;
13+
} else if (body.type === 'BlockStatement') {
14+
return getReturnNode(body);
715
}
816
for (let i = body.length - 1; i >= 0; i -= 1) {
917
if (body[i].type === 'ReturnStatement') {
10-
return body[i].argument;
18+
const arg = body[i].argument;
19+
if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
20+
return getReturnNode(arg);
21+
}
22+
return arg;
1123
}
1224
}
1325
return null;
1426
};
1527

1628
module.exports = {
1729
getReturnNode,
30+
isObject,
1831
};

tests/lib/rules/mapDispatchToProps-prefer-object.js renamed to tests/lib/rules/mapDispatchToProps-returns-object.js

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require('babel-eslint');
22

3-
const rule = require('../../../lib/rules/mapDispatchToProps-prefer-object');
3+
const rule = require('../../../lib/rules/mapDispatchToProps-returns-object');
44
const RuleTester = require('eslint').RuleTester;
55

66
const parserOptions = {
@@ -13,8 +13,11 @@ const parserOptions = {
1313

1414
const ruleTester = new RuleTester({ parserOptions });
1515

16-
ruleTester.run('mapDispatchToProps-prefer-object', rule, {
16+
ruleTester.run('mapDispatchToProps-returns-object', rule, {
1717
valid: [
18+
'const mapDispatchToProps = {}',
19+
'const mapDispatchToProps = null',
20+
'const mapDispatchToProps = actionsMap',
1821
'const mapDispatchToProps = {...actions}',
1922
'const mapDispatchToProps = {anAction: anAction}',
2023
`export default connect(
@@ -24,54 +27,39 @@ ruleTester.run('mapDispatchToProps-prefer-object', rule, {
2427
{ fetchProducts }
2528
)(Products);
2629
`,
27-
'connect(null, null)(App)',
28-
],
29-
invalid: [{
30-
code: 'function mapDispatchToProps () {}',
31-
errors: [
32-
{
33-
message: 'mapDispatchToProps should return object',
34-
},
35-
],
36-
}, {
37-
code: 'const mapDispatchToProps = () => {}',
38-
errors: [
39-
{
40-
message: 'mapDispatchToProps should return object',
41-
},
42-
],
43-
}, {
44-
code: `const mapDispatchToProps = (dispatch) => (
30+
'function mapDispatchToProps () {return {action}}',
31+
`const mapDispatchToProps = (dispatch) => (
4532
{
4633
requestFilteredItems: (client, keyword) => {
4734
dispatch(requestFilteredItems(client, keyword));
4835
}
4936
}
50-
)`,
51-
errors: [
52-
{
53-
message: 'mapDispatchToProps should return object',
54-
},
55-
],
56-
}, {
57-
code: `function mapDispatchToProps(dispatch) {
58-
return { requestFilteredItems: (client, keyword) => {
59-
dispatch(requestFilteredItems(client, keyword));
60-
}
61-
}
62-
}`,
37+
)
38+
`,
39+
`const mapDispatchToProps = dispatch => ({
40+
onDoSomething: () => dispatch(toSomethingElse())
41+
});`,
42+
`const mapDispatchToProps = function(dispatch) {
43+
return { requestFilteredItems: (client, keyword) => {
44+
dispatch(requestFilteredItems(client, keyword));
45+
}
46+
}
47+
}`,
48+
'connect(null, null)(App)',
49+
'function mapDispatchToProps () {return aThing}',
50+
`function mapDispatchToProps(dispatch) {
51+
return { actions: bindActionCreators(actionCreators, dispatch) }
52+
}`,
53+
],
54+
invalid: [{
55+
code: 'function mapDispatchToProps () {}',
6356
errors: [
6457
{
6558
message: 'mapDispatchToProps should return object',
6659
},
6760
],
6861
}, {
69-
code: `const mapDispatchToProps = function(dispatch) {
70-
return { requestFilteredItems: (client, keyword) => {
71-
dispatch(requestFilteredItems(client, keyword));
72-
}
73-
}
74-
}`,
62+
code: 'const mapDispatchToProps = () => {}',
7563
errors: [
7664
{
7765
message: 'mapDispatchToProps should return object',

0 commit comments

Comments
 (0)