diff --git a/docs/rules/no-array-index-key.md b/docs/rules/no-array-index-key.md
index 0b8e6337e3..9f401afbd9 100644
--- a/docs/rules/no-array-index-key.md
+++ b/docs/rules/no-array-index-key.md
@@ -50,6 +50,14 @@ things.reduce((collection, thing, index) => (
things.reduceRight((collection, thing, index) => (
collection.concat()
), []);
+
+React.Children.map(this.props.children, (child, index) => (
+ React.cloneElement(child, { key: index })
+))
+
+Children.forEach(this.props.children, (child, index) => (
+ React.cloneElement(child, { key: index })
+))
```
The following patterns are **not** considered warnings:
diff --git a/lib/rules/no-array-index-key.js b/lib/rules/no-array-index-key.js
index c5b1fe0a32..0d0a7f3fdc 100644
--- a/lib/rules/no-array-index-key.js
+++ b/lib/rules/no-array-index-key.js
@@ -7,6 +7,7 @@
const has = require('has');
const astUtil = require('../util/ast');
const docsUrl = require('../util/docsUrl');
+const pragma = require('../util/pragma');
// ------------------------------------------------------------------------------
// Rule Definition
@@ -47,6 +48,32 @@ module.exports = {
&& indexParamNames.indexOf(node.name) !== -1;
}
+ function isUsingReactChildren(node) {
+ const callee = node.callee;
+ if (
+ !callee
+ || !callee.property
+ || !callee.object
+ ) {
+ return null;
+ }
+
+ const isReactChildMethod = ['map', 'forEach'].indexOf(callee.property.name) > -1;
+ if (!isReactChildMethod) {
+ return null;
+ }
+
+ const obj = callee.object;
+ if (obj && obj.name === 'Children') {
+ return true;
+ }
+ if (obj && obj.object && obj.object.name === pragma.getFromContext(context)) {
+ return true;
+ }
+
+ return false;
+ }
+
function getMapIndexParamName(node) {
const callee = node.callee;
if (callee.type !== 'MemberExpression') {
@@ -59,16 +86,19 @@ module.exports = {
return null;
}
- const firstArg = node.arguments[0];
- if (!firstArg) {
+ const callbackArg = isUsingReactChildren(node)
+ ? node.arguments[1]
+ : node.arguments[0];
+
+ if (!callbackArg) {
return null;
}
- if (!astUtil.isFunctionLikeExpression(firstArg)) {
+ if (!astUtil.isFunctionLikeExpression(callbackArg)) {
return null;
}
- const params = firstArg.params;
+ const params = callbackArg.params;
const indexParamPosition = iteratorFunctionsToIndexParamPosition[callee.property.name];
if (params.length < indexParamPosition + 1) {
diff --git a/tests/lib/rules/no-array-index-key.js b/tests/lib/rules/no-array-index-key.js
index b27932ec67..222f5810ed 100644
--- a/tests/lib/rules/no-array-index-key.js
+++ b/tests/lib/rules/no-array-index-key.js
@@ -89,6 +89,22 @@ ruleTester.run('no-array-index-key', rule, {
{
code: 'foo.reduceRight((a, b, i) => a.concat(), [])'
+ },
+
+ {
+ code: `
+ React.Children.map(this.props.children, (child, index, arr) => {
+ return React.cloneElement(child, { key: child.id });
+ })
+ `
+ },
+
+ {
+ code: `
+ Children.forEach(this.props.children, (child, index, arr) => {
+ return React.cloneElement(child, { key: child.id });
+ })
+ `
}
],
@@ -227,6 +243,43 @@ ruleTester.run('no-array-index-key', rule, {
{
code: 'foo.findIndex((bar, i) => { baz.push(React.createElement(\'Foo\', { key: i })); })',
errors: [{message: 'Do not use Array index in keys'}]
+ },
+
+ {
+ code: `
+ Children.map(this.props.children, (child, index) => {
+ return React.cloneElement(child, { key: index });
+ })
+ `,
+ errors: [{message: 'Do not use Array index in keys'}]
+ },
+
+ {
+ code: `
+ React.Children.map(this.props.children, (child, index) => {
+ return React.cloneElement(child, { key: index });
+ })
+ `,
+ errors: [{message: 'Do not use Array index in keys'}]
+ },
+
+ {
+ code: `
+ Children.forEach(this.props.children, (child, index) => {
+ return React.cloneElement(child, { key: index });
+ })
+ `,
+ errors: [{message: 'Do not use Array index in keys'}]
+ },
+
+ {
+ code: `
+ React.Children.forEach(this.props.children, (child, index) => {
+ return React.cloneElement(child, { key: index });
+ })
+ `,
+ errors: [{message: 'Do not use Array index in keys'}]
}
+
]
});