Skip to content

Commit d1814c1

Browse files
committed
refactor: pr review changes
1 parent 8aec646 commit d1814c1

File tree

3 files changed

+85
-80
lines changed

3 files changed

+85
-80
lines changed

docs/rules/no-render-return-undefined.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@
44

55
<!-- end auto-generated rule header -->
66

7-
> In React 18, components may render undefined, and React will render nothing to the DOM instead of throwing an error. However, accidentally rendering nothing in a component could still cause surprises. This rule will warn if the `return` statement in a React Component returns undefined.
8-
9-
Issue: [#3020](https://github.com/jsx-eslint/eslint-plugin-react/issues/3020)
7+
> Starting in React 18, components may render undefined, and React will render nothing to the DOM instead of throwing an error. However, accidentally rendering nothing in a component could still cause surprises. This rule will warn if the `return` statement in a React Component returns `undefined`.
108
119
## Rule Details
1210

13-
This rule will warn if the `return` statement in a React component returns undefined.
11+
This rule will warn if the `return` statement in a React component returns `undefined`.
1412

1513
Examples of **incorrect** code for this rule:
1614

1715
```jsx
16+
function App() {}
17+
18+
// OR
19+
1820
function App() {
1921
return undefined;
2022
}

lib/rules/no-render-return-undefined.js

+78-75
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,88 @@ const report = require('../util/report');
1212
const variableUtil = require('../util/variable');
1313

1414
const messages = {
15-
returnsUndefined: "Don't return undefined from react components",
15+
returnsUndefined: "Don't return `undefined` from react components",
1616
};
1717

18+
function getReturnValue(context, returnNode) {
19+
const variables = variableUtil.variablesInScope(context);
20+
const returnIdentifierName = returnNode && returnNode.name;
21+
const returnIdentifierVar = variableUtil.getVariable(
22+
variables,
23+
returnIdentifierName
24+
);
25+
26+
if (!returnNode) return undefined;
27+
28+
if (
29+
returnIdentifierVar
30+
&& returnIdentifierVar.defs
31+
&& returnIdentifierVar.defs[0]
32+
) {
33+
const value = returnIdentifierVar.defs[0].node.init;
34+
if (
35+
returnIdentifierVar.defs[0].node
36+
&& returnIdentifierVar.defs[0].node.type === 'VariableDeclarator'
37+
&& value === null
38+
) {
39+
return undefined;
40+
}
41+
return value;
42+
}
43+
44+
switch (returnNode.type) {
45+
case 'LogicalExpression': {
46+
return getReturnValue(context, returnNode.right);
47+
}
48+
case 'ConditionalExpression': {
49+
const possibleReturnValue = [
50+
getReturnValue(context, returnNode.consequent),
51+
getReturnValue(context, returnNode.alternate),
52+
];
53+
const returnsUndefined = possibleReturnValue.some((val) => typeof val === 'undefined');
54+
if (returnsUndefined) return;
55+
return possibleReturnValue;
56+
}
57+
case 'CallExpression': {
58+
if (returnNode.callee.type === 'MemberExpression') {
59+
const calleeObjName = returnNode.callee.object.name;
60+
const calleePropertyName = returnNode.callee.property.name;
61+
const calleeObjNode = variables.find((item) => item && item.name === calleeObjName);
62+
const isCalleeObjArray = calleeObjNode.defs[0].node.init.type === 'ArrayExpression';
63+
const isMapCall = isCalleeObjArray && calleePropertyName === 'map';
64+
if (isMapCall) {
65+
const mapCallback = returnNode.arguments[0];
66+
const mapReturnStatement = mapCallback.body.type === 'BlockStatement'
67+
&& astUtil.findReturnStatement(returnNode.arguments[0]);
68+
const mapReturnNode = (mapReturnStatement && mapReturnStatement.argument) || mapCallback.body;
69+
// console.log('DEBUG', mapReturnNode);
70+
return getReturnValue(context, mapReturnNode);
71+
}
72+
}
73+
const calleeName = returnNode.callee.name;
74+
const calleeNode = variables.find((item) => item && item.name === calleeName);
75+
const calleeDefinitionNode = calleeNode && calleeNode.defs && calleeNode.defs[0] && calleeNode.defs[0].node;
76+
const calleeReturnStatement = astUtil.findReturnStatement(calleeDefinitionNode);
77+
const calleeReturnNode = (calleeReturnStatement && calleeReturnStatement.argument)
78+
|| (calleeDefinitionNode.init && calleeDefinitionNode.init.body);
79+
return getReturnValue(context, calleeReturnNode);
80+
}
81+
case 'ArrayExpression': {
82+
return returnNode.elements;
83+
}
84+
case 'JSXElement': {
85+
return returnNode;
86+
}
87+
default:
88+
return returnNode.value;
89+
}
90+
}
91+
1892
/** @type {import('eslint').Rule.RuleModule} */
1993
module.exports = {
2094
meta: {
2195
docs: {
22-
description: 'Disallow returning undefined from react components',
96+
description: 'Disallow returning `undefined` from react components',
2397
category: 'Best Practices',
2498
recommended: false,
2599
url: docsUrl('no-render-return-undefined'),
@@ -29,89 +103,18 @@ module.exports = {
29103
},
30104

31105
create(context) {
32-
function getReturnValue(returnNode) {
33-
const variables = variableUtil.variablesInScope(context);
34-
const returnIdentifierName = returnNode && returnNode.name;
35-
const returnIdentifierVar = variableUtil.getVariable(
36-
variables,
37-
returnIdentifierName
38-
);
39-
40-
if (!returnNode) return undefined;
41-
42-
if (
43-
returnIdentifierVar
44-
&& returnIdentifierVar.defs
45-
&& returnIdentifierVar.defs[0]
46-
) {
47-
const value = returnIdentifierVar.defs[0].node.init;
48-
if (
49-
returnIdentifierVar.defs[0].node
50-
&& returnIdentifierVar.defs[0].node.type === 'VariableDeclarator'
51-
&& value === null
52-
) {
53-
return undefined;
54-
}
55-
return value;
56-
}
57-
58-
switch (returnNode.type) {
59-
case 'LogicalExpression': {
60-
return getReturnValue(returnNode.right);
61-
}
62-
case 'ConditionalExpression': {
63-
const possibleReturnValue = [getReturnValue(returnNode.consequent), getReturnValue(returnNode.alternate)];
64-
const returnsUndefined = possibleReturnValue.some((val) => val === undefined);
65-
if (returnsUndefined) return undefined;
66-
return possibleReturnValue;
67-
}
68-
case 'CallExpression': {
69-
if (returnNode.callee.type === 'MemberExpression') {
70-
const calleeObjName = returnNode.callee.object.name;
71-
const calleePropertyName = returnNode.callee.property.name;
72-
const calleeObjNode = variables.find((item) => item && item.name === calleeObjName);
73-
const isCalleeObjArray = calleeObjNode.defs[0].node.init.type === 'ArrayExpression';
74-
const isMapCall = isCalleeObjArray && calleePropertyName === 'map';
75-
if (isMapCall) {
76-
const mapCallback = returnNode.arguments[0];
77-
const mapReturnStatement = mapCallback.body.type === 'BlockStatement'
78-
&& astUtil.findReturnStatement(returnNode.arguments[0]);
79-
const mapReturnNode = (mapReturnStatement && mapReturnStatement.argument) || mapCallback.body;
80-
// console.log('DEBUG', mapReturnNode);
81-
return getReturnValue(mapReturnNode);
82-
}
83-
}
84-
const calleeName = returnNode.callee.name;
85-
const calleeNode = variables.find((item) => item && item.name === calleeName);
86-
const calleeDefinitionNode = calleeNode && calleeNode.defs && calleeNode.defs[0] && calleeNode.defs[0].node;
87-
const calleeReturnStatement = astUtil.findReturnStatement(calleeDefinitionNode);
88-
const calleeReturnNode = (calleeReturnStatement && calleeReturnStatement.argument)
89-
|| (calleeDefinitionNode.init && calleeDefinitionNode.init.body);
90-
return getReturnValue(calleeReturnNode);
91-
}
92-
case 'ArrayExpression': {
93-
return returnNode.elements;
94-
}
95-
case 'JSXElement': {
96-
return returnNode;
97-
}
98-
default:
99-
return returnNode.value;
100-
}
101-
}
102-
103106
const isReturningUndefined = (returnStatement) => {
104107
const returnNode = returnStatement && returnStatement.argument;
105108
const returnIdentifierName = returnNode && returnNode.name;
106109

107-
const returnIdentifierValue = getReturnValue(returnNode);
110+
const returnIdentifierValue = getReturnValue(context, returnNode);
108111

109112
const returnsArrayHavingUndefined = Array.isArray(returnIdentifierValue)
110113
&& returnIdentifierValue.some((el) => el && el.type === 'Identifier' && el.name === 'undefined');
111114

112115
return !returnStatement
113116
|| returnIdentifierName === 'undefined'
114-
|| returnIdentifierValue === undefined
117+
|| typeof returnIdentifierValue === 'undefined'
115118
|| (returnIdentifierValue && returnIdentifierValue.name === 'undefined')
116119
|| returnsArrayHavingUndefined;
117120
};

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"test": "npm run unit-test",
1616
"posttest": "aud --production",
1717
"type-check": "tsc",
18-
"unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/rules/no-render-return-undefined.js",
18+
"unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js",
1919
"update:eslint-docs": "eslint-doc-generator"
2020
},
2121
"repository": {

0 commit comments

Comments
 (0)