Skip to content

Commit 38f051f

Browse files
committed
Add new no-children-prop rule (Fixes #720)
Prevents children being passed as props. Children should be actual children. * Add Component to tests for no-children-prop * Add note in docs about recommended behavior * Change syntax in docs for no-children-prop to jsx
1 parent 8b8eba7 commit 38f051f

File tree

5 files changed

+235
-1
lines changed

5 files changed

+235
-1
lines changed

README.md

+1
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-children-prop](docs/rules/no-children-prop.md): Prevent passing children as props
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-children-prop.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Prevent passing of children as props (no-children-prop)
2+
3+
Children should always be actual children, not passed in as a prop.
4+
5+
When using JSX, the children should be nested between the opening and closing
6+
tags. When not using JSX, the children should be passed as the
7+
third argument to `React.createElement`.
8+
9+
## Rule Details
10+
11+
The following patterns are considered warnings:
12+
13+
```jsx
14+
<div children='Children' />
15+
16+
React.createElement("div", { children: 'Children' })
17+
```
18+
19+
The following patterns are not considered warnings:
20+
21+
```jsx
22+
<div>Children</div>
23+
24+
React.createElement("div", {}, 'Children')
25+
```

index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ var rules = {
5050
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
5151
'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
5252
'require-optimization': require('./lib/rules/require-optimization'),
53-
'no-find-dom-node': require('./lib/rules/no-find-dom-node')
53+
'no-find-dom-node': require('./lib/rules/no-find-dom-node'),
54+
'no-children-prop': require('./lib/rules/no-children-prop')
5455
};
5556

5657
var ruleNames = Object.keys(rules);

lib/rules/no-children-prop.js

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* @fileoverview Prevent passing of children as props
3+
* @author Benjamin Stepp
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Helpers
9+
// ------------------------------------------------------------------------------
10+
11+
/**
12+
* Checks if the node is a createElement call with a props literal.
13+
* @param {ASTNode} node - The AST node being checked.
14+
* @returns {Boolean} - True if node is a createElement call with a props
15+
* object literal, False if not.
16+
*/
17+
function isCreateElementWithProps(node) {
18+
return node.callee
19+
&& node.callee.type === 'MemberExpression'
20+
&& node.callee.property.name === 'createElement'
21+
&& node.arguments.length > 1
22+
&& node.arguments[1].type === 'ObjectExpression';
23+
}
24+
25+
// ------------------------------------------------------------------------------
26+
// Rule Definition
27+
// ------------------------------------------------------------------------------
28+
29+
module.exports = {
30+
meta: {
31+
docs: {},
32+
schema: []
33+
},
34+
create: function(context) {
35+
return {
36+
JSXAttribute: function(node) {
37+
if (node.name.name !== 'children') {
38+
return;
39+
}
40+
41+
context.report({
42+
node: node,
43+
message: 'Do not pass children as props. Instead, nest children between the opening and closing tags.'
44+
});
45+
},
46+
CallExpression: function(node) {
47+
if (!isCreateElementWithProps(node)) {
48+
return;
49+
}
50+
51+
var props = node.arguments[1].properties;
52+
var childrenProp = props.find(function(prop) {
53+
return prop.key.name === 'children';
54+
});
55+
56+
if (childrenProp) {
57+
context.report({
58+
node: node,
59+
message: 'Do not pass children as props. Instead, pass them as the third argument to React.createElement.'
60+
});
61+
}
62+
}
63+
};
64+
}
65+
};

tests/lib/rules/no-children-prop.js

+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* @fileoverview Tests for no-children-prop
3+
* @author Benjamin Stepp
4+
*/
5+
6+
'use strict';
7+
8+
// -----------------------------------------------------------------------------
9+
// Requirements
10+
// -----------------------------------------------------------------------------
11+
12+
var rule = require('../../../lib/rules/no-children-prop');
13+
var RuleTester = require('eslint').RuleTester;
14+
15+
var parserOptions = {
16+
ecmaVersion: 6,
17+
ecmaFeatures: {
18+
jsx: true
19+
}
20+
};
21+
22+
// -----------------------------------------------------------------------------
23+
// Tests
24+
// -----------------------------------------------------------------------------
25+
26+
var ruleTester = new RuleTester();
27+
ruleTester.run('no-children-prop', rule, {
28+
valid: [
29+
{
30+
code: '<div />;',
31+
parserOptions: parserOptions
32+
},
33+
{
34+
code: '<div></div>;',
35+
parserOptions: parserOptions
36+
},
37+
{
38+
code: 'React.createElement("div", {});',
39+
parserOptions: parserOptions
40+
},
41+
{
42+
code: 'React.createElement("div", undefined);',
43+
parserOptions: parserOptions
44+
},
45+
{
46+
code: '<div>Children</div>;',
47+
parserOptions: parserOptions
48+
},
49+
{
50+
code: 'React.createElement("div", {}, "Children");',
51+
parserOptions: parserOptions
52+
},
53+
{
54+
code: 'React.createElement("div", undefined, "Children");',
55+
parserOptions: parserOptions
56+
},
57+
{
58+
code: '<div><div /></div>;',
59+
parserOptions: parserOptions
60+
},
61+
{
62+
code: 'React.createElement("div", {}, React.createElement("div"));',
63+
parserOptions: parserOptions
64+
},
65+
{
66+
code: 'React.createElement("div", undefined, React.createElement("div"));',
67+
parserOptions: parserOptions
68+
},
69+
{
70+
code: '<div><div /><div /></div>;',
71+
parserOptions: parserOptions
72+
},
73+
{
74+
code: 'React.createElement("div", {}, [React.createElement("div"), React.createElement("div")]);',
75+
parserOptions: parserOptions
76+
},
77+
{
78+
code: 'React.createElement("div", undefined, [React.createElement("div"), React.createElement("div")]);',
79+
parserOptions: parserOptions
80+
},
81+
{
82+
code: '<MyComponent />',
83+
parserOptions: parserOptions
84+
},
85+
{
86+
code: '<MyComponent>Children</MyComponent>',
87+
parserOptions: parserOptions
88+
}
89+
],
90+
invalid: [
91+
{
92+
code: '<div children="Children" />;',
93+
errors: [{message: 'Do not pass children as props.'}],
94+
parserOptions: parserOptions
95+
},
96+
{
97+
code: '<div children={<div />} />;',
98+
errors: [{message: 'Do not pass children as props.'}],
99+
parserOptions: parserOptions
100+
},
101+
{
102+
code: '<div children={[<div />, <div />]} />;',
103+
errors: [{message: 'Do not pass children as props.'}],
104+
parserOptions: parserOptions
105+
},
106+
{
107+
code: '<div children="Children">Children</div>;',
108+
errors: [{message: 'Do not pass children as props.'}],
109+
parserOptions: parserOptions
110+
},
111+
{
112+
code: 'React.createElement("div", {children: "Children"});',
113+
errors: [{message: 'Do not pass children as props.'}],
114+
parserOptions: parserOptions
115+
},
116+
{
117+
code: 'React.createElement("div", {children: "Children"}, "Children");',
118+
errors: [{message: 'Do not pass children as props.'}],
119+
parserOptions: parserOptions
120+
},
121+
{
122+
code: 'React.createElement("div", {children: React.createElement("div")});',
123+
errors: [{message: 'Do not pass children as props.'}],
124+
parserOptions: parserOptions
125+
},
126+
{
127+
code: 'React.createElement("div", {children: [React.createElement("div"), React.createElement("div")]});',
128+
errors: [{message: 'Do not pass children as props.'}],
129+
parserOptions: parserOptions
130+
},
131+
{
132+
code: '<MyComponent children="Children" />',
133+
errors: [{message: 'Do not pass children as props.'}],
134+
parserOptions: parserOptions
135+
},
136+
{
137+
code: 'React.createElement(MyComponent, {children: "Children"});',
138+
errors: [{message: 'Do not pass children as props.'}],
139+
parserOptions: parserOptions
140+
}
141+
]
142+
});

0 commit comments

Comments
 (0)