Skip to content

Commit 6dc7803

Browse files
akulsr0ljharb
authored andcommitted
[Fix] jsx-key: incorrect behavior for checkKeyMustBeforeSpread with map callbacks
1 parent 393bfa2 commit 6dc7803

File tree

3 files changed

+41
-18
lines changed

3 files changed

+41
-18
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1010
* [`boolean-prop-naming`]: avoid a crash with a spread prop ([#3733][] @ljharb)
1111
* [`jsx-boolean-value`]: `assumeUndefinedIsFalse` with `never` must not allow explicit `true` value ([#3757][] @6uliver)
1212
* [`no-object-type-as-default-prop`]: enable rule for components with many parameters ([#3768][] @JulienR1)
13+
* [`jsx-key`]: incorrect behavior for checkKeyMustBeforeSpread with map callbacks ([#3769][] @akulsr0)
1314

15+
[#3769]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3769
1416
[#3768]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3768
1517
[#3762]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3762
1618
[#3757]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3757

lib/rules/jsx-key.js

+24-18
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,31 @@ module.exports = {
7373
const reactPragma = pragmaUtil.getFromContext(context);
7474
const fragmentPragma = pragmaUtil.getFragmentFromContext(context);
7575

76+
function isKeyAfterSpread(attributes) {
77+
let hasFoundSpread = false;
78+
return attributes.some((attribute) => {
79+
if (attribute.type === 'JSXSpreadAttribute') {
80+
hasFoundSpread = true;
81+
return false;
82+
}
83+
if (attribute.type !== 'JSXAttribute') {
84+
return false;
85+
}
86+
return hasFoundSpread && propName(attribute) === 'key';
87+
});
88+
}
89+
7690
function checkIteratorElement(node) {
77-
if (node.type === 'JSXElement' && !hasProp(node.openingElement.attributes, 'key')) {
78-
report(context, messages.missingIterKey, 'missingIterKey', {
79-
node,
80-
});
91+
if (node.type === 'JSXElement') {
92+
if (!hasProp(node.openingElement.attributes, 'key')) {
93+
report(context, messages.missingIterKey, 'missingIterKey', { node });
94+
} else {
95+
const attrs = node.openingElement.attributes;
96+
97+
if (checkKeyMustBeforeSpread && isKeyAfterSpread(attrs)) {
98+
report(context, messages.keyBeforeSpread, 'keyBeforeSpread', { node });
99+
}
100+
}
81101
} else if (checkFragmentShorthand && node.type === 'JSXFragment') {
82102
report(context, messages.missingIterKeyUsePrag, 'missingIterKeyUsePrag', {
83103
node,
@@ -115,20 +135,6 @@ module.exports = {
115135
return returnStatements;
116136
}
117137

118-
function isKeyAfterSpread(attributes) {
119-
let hasFoundSpread = false;
120-
return attributes.some((attribute) => {
121-
if (attribute.type === 'JSXSpreadAttribute') {
122-
hasFoundSpread = true;
123-
return false;
124-
}
125-
if (attribute.type !== 'JSXAttribute') {
126-
return false;
127-
}
128-
return hasFoundSpread && propName(attribute) === 'key';
129-
});
130-
}
131-
132138
/**
133139
* Checks if the given node is a function expression or arrow function,
134140
* and checks if there is a missing key prop in return statement's arguments

tests/lib/rules/jsx-key.js

+15
Original file line numberDiff line numberDiff line change
@@ -409,5 +409,20 @@ ruleTester.run('jsx-key', rule, {
409409
{ messageId: 'missingIterKey' },
410410
],
411411
},
412+
{
413+
code: `
414+
const TestCase = () => {
415+
const list = [1, 2, 3, 4, 5];
416+
417+
return (
418+
<div>
419+
{list.map(x => <div {...spread} key={x} />)}
420+
</div>
421+
);
422+
};
423+
`,
424+
options: [{ checkKeyMustBeforeSpread: true }],
425+
errors: [{ messageId: 'keyBeforeSpread' }],
426+
},
412427
]),
413428
});

0 commit comments

Comments
 (0)