Skip to content

Commit

Permalink
[Fix] jsx-key: Ignore elements inside React.Children.toArray()
Browse files Browse the repository at this point in the history
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 #1574.
  • Loading branch information
silvenon authored and ljharb committed Dec 10, 2017
1 parent 2575bba commit 0bd9819
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,9 +8,11 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
### Fixed
* [`no-unknown-property`]: add `dialog` attributes ([#3436][] @ljharb)
* [`no-arrow-function-lifecycle`]: when converting from an arrow, remove the semi and wrapping parens ([#3337][] @ljharb)
* [`jsx-key`]: Ignore elements inside `React.Children.toArray()` ([#1591][] @silvenon)

[#3436]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3436
[#3337]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3337
[#1591]: https://github.com/jsx-eslint/eslint-plugin-react/pull/1591

## [7.31.8] - 2022.09.08

Expand Down
33 changes: 32 additions & 1 deletion lib/rules/jsx-key.js
Expand Up @@ -155,10 +155,33 @@ module.exports = {
}
}

const childrenToArraySelector = `:matches(
CallExpression
[callee.object.object.name=${reactPragma}]
[callee.object.property.name=Children]
[callee.property.name=toArray],
CallExpression
[callee.object.name=Children]
[callee.property.name=toArray]
)`.replace(/\s/g, '');
let isWithinChildrenToArray = false;

const seen = new WeakSet();

return {
[childrenToArraySelector]() {
isWithinChildrenToArray = true;
},

[`${childrenToArraySelector}:exit`]() {
isWithinChildrenToArray = false;
},

'ArrayExpression, JSXElement > JSXElement'(node) {
if (isWithinChildrenToArray) {
return;
}

const jsx = (node.type === 'ArrayExpression' ? node.elements : node.parent.children).filter((x) => x && x.type === 'JSXElement');
if (jsx.length === 0) {
return;
Expand Down Expand Up @@ -205,7 +228,7 @@ module.exports = {
},

JSXFragment(node) {
if (!checkFragmentShorthand) {
if (!checkFragmentShorthand || isWithinChildrenToArray) {
return;
}

Expand All @@ -226,6 +249,10 @@ module.exports = {
CallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"],\
OptionalCallExpression[callee.type="MemberExpression"][callee.property.name="map"],\
OptionalCallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"]'(node) {
if (isWithinChildrenToArray) {
return;
}

const fn = node.arguments.length > 0 && node.arguments[0];
if (!fn || !astUtil.isFunctionLikeExpression(fn)) {
return;
Expand All @@ -238,6 +265,10 @@ module.exports = {

// Array.from
'CallExpression[callee.type="MemberExpression"][callee.property.name="from"]'(node) {
if (isWithinChildrenToArray) {
return;
}

const fn = node.arguments.length > 1 && node.arguments[1];
if (!astUtil.isFunctionLikeExpression(fn)) {
return;
Expand Down
23 changes: 23 additions & 0 deletions tests/lib/rules/jsx-key.js
Expand Up @@ -176,6 +176,29 @@ ruleTester.run('jsx-key', rule, {
`,
features: ['types', 'no-babel-old'],
},
{ code: 'React.Children.toArray([1, 2 ,3].map(x => <App />));' },
{
code: `
import { Children } from "react";
Children.toArray([1, 2 ,3].map(x => <App />));
`,
},
{
// TODO: uncomment the two lines below
code: `
import Act from 'react';
import { Children as ReactChildren } from 'react';
const { Children } = Act;
const { toArray } = Children;
Act.Children.toArray([1, 2 ,3].map(x => <App />));
Children.toArray([1, 2 ,3].map(x => <App />));
// ReactChildren.toArray([1, 2 ,3].map(x => <App />));
// toArray([1, 2 ,3].map(x => <App />));
`,
settings,
},
]),
invalid: parsers.all([
{
Expand Down

0 comments on commit 0bd9819

Please sign in to comment.