Skip to content

Commit 8beb2aa

Browse files
silvenonljharb
authored andcommitted
[Fix] jsx-key: Ignore elements inside React.Children.toArray()
jsx-key rule should always succeed if we're inside React.Children.toArray() because omitting the key there doesn't cause a React warning. Fixes jsx-eslint#1574.
1 parent 2575bba commit 8beb2aa

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
88
### Fixed
99
* [`no-unknown-property`]: add `dialog` attributes ([#3436][] @ljharb)
1010
* [`no-arrow-function-lifecycle`]: when converting from an arrow, remove the semi and wrapping parens ([#3337][] @ljharb)
11+
* [`jsx-key`]: Ignore elements inside `React.Children.toArray()` ([#1591][] @silvenon)
1112

1213
[#3436]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3436
1314
[#3337]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3337
15+
[#1591]: https://github.com/jsx-eslint/eslint-plugin-react/pull/1591
1416

1517
## [7.31.8] - 2022.09.08
1618

lib/rules/jsx-key.js

+32-1
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,33 @@ module.exports = {
155155
}
156156
}
157157

158+
const childrenToArraySelector = `:matches(
159+
CallExpression
160+
[callee.object.object.name=${reactPragma}]
161+
[callee.object.property.name=Children]
162+
[callee.property.name=toArray],
163+
CallExpression
164+
[callee.object.name=Children]
165+
[callee.property.name=toArray]
166+
)`.replace(/\s/g, '');
167+
let isWithinChildrenToArray = false;
168+
158169
const seen = new WeakSet();
159170

160171
return {
172+
[childrenToArraySelector]() {
173+
isWithinChildrenToArray = true;
174+
},
175+
176+
[`${childrenToArraySelector}:exit`]() {
177+
isWithinChildrenToArray = false;
178+
},
179+
161180
'ArrayExpression, JSXElement > JSXElement'(node) {
181+
if (isWithinChildrenToArray) {
182+
return;
183+
}
184+
162185
const jsx = (node.type === 'ArrayExpression' ? node.elements : node.parent.children).filter((x) => x && x.type === 'JSXElement');
163186
if (jsx.length === 0) {
164187
return;
@@ -205,7 +228,7 @@ module.exports = {
205228
},
206229

207230
JSXFragment(node) {
208-
if (!checkFragmentShorthand) {
231+
if (!checkFragmentShorthand || isWithinChildrenToArray) {
209232
return;
210233
}
211234

@@ -226,6 +249,10 @@ module.exports = {
226249
CallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"],\
227250
OptionalCallExpression[callee.type="MemberExpression"][callee.property.name="map"],\
228251
OptionalCallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"]'(node) {
252+
if (isWithinChildrenToArray) {
253+
return;
254+
}
255+
229256
const fn = node.arguments.length > 0 && node.arguments[0];
230257
if (!fn || !astUtil.isFunctionLikeExpression(fn)) {
231258
return;
@@ -238,6 +265,10 @@ module.exports = {
238265

239266
// Array.from
240267
'CallExpression[callee.type="MemberExpression"][callee.property.name="from"]'(node) {
268+
if (isWithinChildrenToArray) {
269+
return;
270+
}
271+
241272
const fn = node.arguments.length > 1 && node.arguments[1];
242273
if (!astUtil.isFunctionLikeExpression(fn)) {
243274
return;

tests/lib/rules/jsx-key.js

+27
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,33 @@ ruleTester.run('jsx-key', rule, {
176176
`,
177177
features: ['types', 'no-babel-old'],
178178
},
179+
{ code: 'React.Children.toArray([1, 2 ,3].map(x => <App />));' },
180+
{
181+
code: `
182+
import { Children } from "react";
183+
Children.toArray([1, 2 ,3].map(x => <App />));
184+
`,
185+
},
186+
{
187+
// TODO: uncomment the commented lines below
188+
code: `
189+
import Act from 'react';
190+
import { Children as ReactChildren } from 'react';
191+
192+
const { Children } = Act;
193+
const { toArray } = Children;
194+
195+
Act.Children.toArray([1, 2 ,3].map(x => <App />));
196+
Act.Children.toArray(Array.from([1, 2 ,3], x => <App />));
197+
Children.toArray([1, 2 ,3].map(x => <App />));
198+
Children.toArray(Array.from([1, 2 ,3], x => <App />));
199+
// ReactChildren.toArray([1, 2 ,3].map(x => <App />));
200+
// ReactChildren.toArray(Array.from([1, 2 ,3], x => <App />));
201+
// toArray([1, 2 ,3].map(x => <App />));
202+
// toArray(Array.from([1, 2 ,3], x => <App />));
203+
`,
204+
settings,
205+
},
179206
]),
180207
invalid: parsers.all([
181208
{

0 commit comments

Comments
 (0)